Skip to content

Phase 4: Feature flags, root engine facade, and modular docs#81

Merged
leynos merged 11 commits intomainfrom
terragon/feature-flags-implementation-phase-4-dk5292
Jan 5, 2026
Merged

Phase 4: Feature flags, root engine facade, and modular docs#81
leynos merged 11 commits intomainfrom
terragon/feature-flags-implementation-phase-4-dk5292

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jan 2, 2026

Summary

  • Phase 4 milestones: feature flags gateable runtime, root engine facade crate, and decomposition of design/docs into modular artifacts. Docs now live under docs/wildside-engine-design with a dedicated Phase-4 feature-flags ExecPlan artifact at docs/execplans/phase-4-feature-flags.md.
  • A new root façade crate is introduced: wildside-engine, which re-exports core types and conditionally exposes solver/store implementations via feature flags.
  • The ExecPlan and architecture documentation have been decomposed into focused modules to improve readability, reviewability, and targeted testing.
  • The PR also introduces a placeholder OR-Tools solver crate and wires feature flags through the workspace to support --all-features builds.
  • The CLI, core exports, and tests are oriented around feature gating to enable/disable solver and store backends at compile-time.
  • This update continues to gate the SQLite store behind the store-sqlite feature and adjust exports accordingly, so SqlitePoiStore is only available when that feature is enabled.
  • Documentation and roadmap updated to reflect Phase-4 gating, with explicit ExecPlan artifact for feature flags.
  • Tests expanded to cover enabled/disabled feature paths and missing-feature scenarios.
  • Revision note: Updated to reflect Phase 4 feature-flag implementation and design-doc decomposition.

Changes

  • New root engine facade crate: wildside-engine, re-exporting core types and conditionally exposing solver/store implementations via feature flags.
  • Workspace feature flags added/defined at root: solver-vrp, solver-ortools, store-sqlite.
  • Introduced wildside-solver-ortools crate as a placeholder backend and wired into the workspace so --all-features builds succeed.
  • Core store refactor: moved SQLite-backed storage behind the store-sqlite feature, with dedicated store module layout and spatial index helpers.
  • Reworked store exports: wildside-core now exports PoiStore unconditionally and SqlitePoiStore/SqlitePoiStoreError only when store-sqlite is enabled.
  • CLI wiring updated: CLI respects feature flags for solver and store selection, introduces a MissingFeature error variant for unsupported paths, and guards usage of SqlitePoiStore behind #[cfg(feature = "store-sqlite")].
  • OR-Tools solver placeholder: wildside-solver-ortools crate added to satisfy --all-features builds and provide a documented surface for future integration.
  • Documentation and roadmap: Phase-4 ExecPlan artifact added; engine-design and deployment docs updated to reflect feature-gated paths and defaults. Documentation decomposed into modular files under docs/wildside-engine-design and a dedicated docs/execplans Phase-4 feature-flags page.
  • Tests expanded: unit and behavioural tests extended to cover enabled/disabled paths, including feature-wiring in CLI code paths.
  • Revision note: Updated to reflect Phase 4 feature-flag implementation and design-doc decomposition.

Rationale

  • Feature flags enable selecting solver and store implementations at compile time without breaking defaults, while allowing targeted testing and clean deployment of enabled paths. The CLI gracefully handles missing features and guides users toward the correct feature-set for their build. Decomposing docs improves readability, review efficiency, and future maintainability.

Plan of Work (highlights)

  • Define root-level features solver-vrp, solver-ortools, and store-sqlite and propagate through workspace crates.
  • Implement a placeholder wildside-solver-ortools crate behind the solver-ortools feature and wire it through the workspace.
  • Gate the SQLite store in wildside-core. Introduce a feature such as store-sqlite (or sqlite if you want a shorter internal name) and make rusqlite, serde, serde_json, and bincode optional dependencies that are activated by that feature. Move SQLite-specific types (SqlitePoiStore, SqlitePoiStoreError, spatial index helpers) into a module compiled only when that feature is enabled, and keep PoiStore and any in-memory/test stores available without it. Update wildside-core/src/lib.rs to conditionally re-export the SQLite store and to add doc(cfg(...)) hints where appropriate. Adjust existing unit tests in wildside-core/src/store.rs so SQLite tests are only compiled when the feature is enabled.
  • After that, update wildside-cli to respect the feature flags. Use #[cfg(feature = "solver-vrp")] and #[cfg(feature = "solver-ortools")] to select the solver implementation in wildside-cli/src/solve.rs (a small factory module can keep the conditional code contained). Gate SqlitePoiStore usage on the store feature and introduce an explicit error variant in wildside_cli/src/error.rs for missing feature support, so the CLI fails gracefully when built without a required feature. Add feature forwarding in wildside-cli/Cargo.toml so its features map to the root features via dep: dependencies, keeping the root manifest as the source of truth.
  • Add tests for both happy and unhappy paths. Use rstest for unit-level tests that exercise solver/store selection, and rstest-bdd for behavioural tests that assert the user-visible errors when a feature is disabled. Because the feature combinations are compile-time, plan to run targeted test commands for specific feature sets (for example, --no-default-features with solver-only or store-only). Ensure all test modules and scenarios are guarded with the same #[cfg(feature = "...")] selectors so they only compile under the matching feature set.
  • Finally, record the feature flag decisions in docs/wildside-engine-design.md, update any testing notes affected by the rstest-bdd version bump, and mark the Phase 4 feature flag entry in docs/roadmap.md as done. Run formatting, linting, and tests via the Makefile and keep logs for review.

Validation & Acceptance Criteria

  • The ExecPlan doc exists (docs/execplans/phase-4-feature-flags.md) and remains accessible, now complemented by implemented code/tests.
  • The workspace builds with default features (VRP solver and SQLite store) and the CLI respects feature gating at runtime.
  • Optional features compile cleanly under --all-features, with tests covering enabled/disabled paths.
  • Feature-flag wiring is reflected in docs/design and roadmap references, and future PRs implement the outlined steps.
  • Tests demonstrate correct handling of missing features via CLI errors.

How to Review

  • Confirm scope aligns with Phase 4 goals (feature flags for solver and store, root-engine facade, tests, and deployment steps).
  • Check gating points and feature names (solver-vrp, solver-ortools, store-sqlite) and how they affect CLI/core wiring.
  • Verify explicit validation steps and references to complementary docs are present.

Artifacts & Notes

  • This PR introduces substantial planning/documentation artifacts in addition to code changes (root engine facade, core refactor, CLI gating, OR-Tools stub, new tests).
  • The Phase-4 plan guides subsequent implementation PRs, tests, and deployments.

Interfaces & Dependencies

  • Feature flags defined at root manifest with these names:
    • solver-vrp: enables wildside-solver-vrp.
    • solver-ortools: enables wildside-solver-ortools.
    • store-sqlite: enables the SQLite-backed POI store.
  • The root package should expose, at minimum:
    • pub use wildside_core::{SolveRequest, SolveResponse, Solver};
    • #[cfg(feature = "store-sqlite")] pub use wildside_core::SqlitePoiStore;
  • wildside-core must compile without store-sqlite, and only expose SqlitePoiStore and its errors when the feature is enabled. wildside-cli should compile under any feature combination, returning a dedicated CliError when a solver or store feature is missing.

Revision note

  • Updated progress, recorded the workspace dependency surprise, and logged the feature-gating decisions. Remaining work is the full formatting, linting, and test validation cycle plus any fixes they uncover.

Task

Add a detailed ExecPlan for Phase 4 feature flags introducing gated
compilation for solvers and stores. This living document outlines the
purpose, progress checklist, decisions, testing, validation criteria,
and integration steps to implement feature flags that let consumers
choose solver and store implementations while maintaining a stable
default build. It serves as the authoritative guide for the feature
flag implementation and verification process.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 2, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Summary by CodeRabbit

  • New Features

    • Optional OR-Tools solver added; workspace and CLI feature flags let you select solver and SQLite store (CLI defaults enable VRP + SQLite).
  • Bug Fixes

    • New SolveError::NotImplemented and clearer CLI MissingFeature/IO error messages for disabled features or missing files.
  • Documentation

    • Major design, roadmap and user‑guide additions covering feature flags, ingestion, scoring, architecture and migration.
  • Tests / CI

    • New feature‑flag tests and CI matrix exercising solver/store feature combinations.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Summarise: Add an optional OR‑Tools solver crate and feature flags; split the monolithic SQLite store into modular spatial_index/sqlite modules; gate SQLite store, ingestion and solver selection behind features; introduce CliError::MissingFeature; update tests, docs, CI matrix and Makefile.

Changes

Cohort / File(s) Summary
Workspace & Manifests
Cargo.toml, wildside-core/Cargo.toml, wildside-cli/Cargo.toml, wildside-solver-ortools/Cargo.toml
Add wildside-solver-ortools workspace member; add solver-ortools and store-sqlite features; wire optional workspace deps and feature mappings; bump rstest-bdd deps; adjust default features.
Core store split
wildside-core/src/store.rs (deleted), wildside-core/src/store/mod.rs, wildside-core/src/store/spatial_index.rs, wildside-core/src/store/sqlite.rs
Remove monolithic store.rs; add PoiStore trait; implement spatial_index persistence; provide feature‑gated sqlite implementation, index I/O, error types and validation.
Public API facade
src/lib.rs, wildside-core/src/lib.rs
Replace simple greet with re‑export facade; forbid unsafe; gate SqlitePoiStore and errors behind store-sqlite; conditionally re‑export solver types (solver-vrp, solver-ortools).
CLI feature gating & errors
wildside-cli/src/error.rs, wildside-cli/src/lib.rs, wildside-cli/src/solve.rs
Add CliError::MissingFeature; gate ingestion, Wikidata helpers and store construction on store-sqlite; add make_store_and_deps and build_solver_with_features to assemble store/provider/scorer and select solver or return MissingFeature.
CLI tests & BDD
wildside-cli/src/tests/*, wildside-cli/tests/features/feature_flags.feature
Add BDD and unit tests asserting MissingFeature when store-sqlite is disabled; add fixtures, helpers and step definitions; gate tests appropriately.
Core test support gating
wildside-core/src/test_support.rs, wildside-core/tests/sqlite_poi_store_behaviour.rs
Restrict sqlite test utilities and related tests to compile only when store-sqlite is enabled by tightening cfg attributes.
New solver crate
wildside-solver-ortools/src/lib.rs
Add placeholder OrtoolsSolver<S,T,C> implementing Solver that returns SolveError::NotImplemented (feature‑gated optional backend stub).
Docs & roadmap
docs/*, docs/wildside-engine-design/*, docs/execplans/phase-4-feature-flags.md, docs/roadmap.md, docs/users-guide.md
Add Phase‑4 feature‑flags plan; split and expand engine design docs; update roadmap and users guide to document gated store/solver availability and acceptance criteria.
CI & Makefile
.github/workflows/ci.yml, Makefile
Add feature-matrix CI job exercising multiple feature permutations; add TEST_FLAGS Makefile variable and pass it into test invocation.
Misc CLI test gating
wildside-cli/src/tests/pipeline.rs, wildside-cli/src/tests/pipeline_steps.rs, wildside-cli/src/tests/steps.rs, wildside-cli/src/tests/unit.rs, wildside-cli/src/tests/mod.rs
Add cfg attributes to gate various test modules behind store-sqlite, and register new test modules for feature-flag scenarios.
API surface changes
wildside-core/src/solver.rs, src/lib.rs
Add SolveError::NotImplemented; remove greet(); expose core types via facade and conditional solver/store re-exports.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CLI as CLI
  participant Builder as SolverBuilder
  participant Store as SqlitePoiStore
  participant Provider as HttpTravelTimeProvider
  participant Scorer as UserRelevanceScorer
  participant Solver as Solver

  CLI->>Builder: build_solver(config)
  alt store-sqlite enabled
    Builder->>Store: open(database_path, index_path)
    alt open succeeds
      Builder->>Provider: construct travel-time provider
      Builder->>Scorer: construct scorer
      Builder->>Builder: select solver by features
      alt solver-vrp enabled
        Builder->>Solver: construct VrpSolver(store, provider, scorer)
        Solver-->>Builder: Ok(Box<dyn Solver>)
      else solver-ortools enabled
        Builder->>Solver: construct OrtoolsSolver(store, provider, scorer)
        Solver-->>Builder: Ok(Box<dyn Solver>)
      else no solver enabled
        Builder-->>CLI: Err(MissingFeature{"solver-vrp or solver-ortools","solve"})
      end
    else open failed
      Builder-->>CLI: Err(SqlitePoiStoreError::OpenDatabase)
    end
  else store-sqlite disabled
    Builder-->>CLI: Err(MissingFeature{"store-sqlite","solve/ingest"})
  end
  Builder-->>CLI: return solver or error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

Flags unfurl, crates rearrange,
Stores split out, and solvers change,
Tests assert the gate's demand,
Docs and CI take a new stand,
🚦🔧

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the primary change: introduction of Phase 4 feature flags, a root engine façade crate, and modular documentation decomposition.
Description check ✅ Passed The description comprehensively relates to the changeset, detailing Phase 4 milestones, feature-flag implementation, root crate introduction, documentation refactoring, testing strategies, and validation criteria.
Docstring Coverage ✅ Passed Docstring coverage is 82.43% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch terragon/feature-flags-implementation-phase-4-dk5292

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4fe602 and 9fe8b44.

📒 Files selected for processing (1)
  • wildside-cli/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make check-fmt, make lint, and make test before committing in Rust projects.
make check-fmt executes cargo fmt --workspace -- --check to validate formatting across the entire workspace without modifying files.
make lint executes cargo clippy --workspace --all-targets --all-features -- -D warnings to lint every target with all features enabled and deny all Clippy warnings.
make test executes cargo test --workspace to run the full workspace test suite.
Clippy warnings MUST be disallowed in Rust code.
Fix any warnings emitted during tests in the Rust code itself rather than silencing them.
Where a Rust function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a Rust function has too many parameters, group related parameters in meaningfully named structs.
Where a Rust function is returning a large error, consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality in Rust. Run both before and after making any change.
Every Rust module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings in Rust.
Handle errors with the Result type instead of panicking in Rust where feasible.
Avoid unsafe code in Rust unless absolutely necessary, and document any usage clearly.
Place function attributes after doc comments in Rust.
Do not use return in single-line Rust functions.
Use predicate functions for conditional criteria in Rust with more than two branches.
Rust lints must not be silenced except as a last resort.
Rust lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow in Rust.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated Rust tests with `#[rstest(...)...

Files:

  • wildside-cli/src/lib.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • wildside-cli/src/lib.rs
🧬 Code graph analysis (1)
wildside-cli/src/lib.rs (6)
wildside-core/src/store/spatial_index.rs (1)
  • write_spatial_index (90-95)
wildside-data/src/wikidata/etl/mod.rs (1)
  • extract_linked_entity_claims (153-198)
wildside-data/src/wikidata/store/persistence.rs (1)
  • persist_claims_to_path (285-295)
wildside-data/src/ingest/mod.rs (1)
  • ingest_osm_pbf_report (149-202)
wildside-data/src/ingest/sqlite.rs (1)
  • persist_pois_to_sqlite (97-122)
wildside-cli/src/solve.rs (2)
  • into_config (105-108)
  • try_from (160-187)
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (2)
wildside-cli/src/lib.rs (2)

70-84: LGTM – drop(args) is a superior solution to the past comment's concern.

The explicit drop(args) call at line 73 clarifies intent when store-sqlite is disabled and eliminates the need for lint suppression entirely. This approach is preferable to the previously suggested #[expect(unused_variables, ...)] attribute because it actively uses the variable rather than suppressing the warning, aligning with the guideline: "Lints must not be silenced except as a last resort."

The feature-gating structure correctly returns MissingFeature when the store is unavailable and delegates to the ingestion flow when enabled.


287-291: Excellent use of a unit type to maintain API consistency across feature permutations.

The doc comment clearly explains that IngestOutcome becomes a unit type when store-sqlite is disabled, preserving the run_ingest function signature whilst ensuring ingestion always fails with MissingFeature. This pattern allows the codebase to compile cleanly in all feature combinations without duplicating function declarations.


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

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jan 2, 2026

Reviewer's Guide

Introduces a root facade crate with workspace-wide Phase 4 feature flags (solver-vrp, solver-ortools, store-sqlite), gates SQLite store and solver wiring in core and CLI behind those features, adds an OR-Tools placeholder solver crate, and updates docs/tests to exercise feature-enabled and disabled paths including CLI error behavior.

Sequence diagram for CLI solve feature-flagged solver and store selection

sequenceDiagram
    actor User
    participant CLI as wildside_cli
    participant Builder as DefaultSolveSolverBuilder
    participant Store as SqlitePoiStore
    participant Vrp as VrpSolver
    participant Ortools as OrtoolsSolver

    User->>CLI: run_solve(args)
    CLI->>Builder: build(&SolveConfig)

    alt feature store-sqlite enabled
        Builder->>Store: open(pois.db, pois.rstar)
        Store-->>Builder: SqlitePoiStore or error
        alt open ok
            Builder->>Builder: build_solver_with_features(store, provider, scorer)
            alt features solver-vrp && solver-ortools disabled or enabled
                Note over Builder,Vrp: cfg(all(feature = store-sqlite, feature = solver-vrp))
                Builder-->>CLI: Ok(Box<VrpSolver>)
            else only solver-ortools enabled
                Note over Builder,Ortools: cfg(all(feature = store-sqlite, not(feature = solver-vrp), feature = solver-ortools))
                Builder-->>CLI: Ok(Box<OrtoolsSolver>)
            else no solver features enabled
                Builder-->>CLI: Err(CliError::MissingFeature{ feature: "solver-vrp or solver-ortools", action: "solve" })
            end
        else open error
            Builder-->>CLI: Err(CliError::OpenPoiStore)
        end
    else feature store-sqlite disabled
        Builder-->>CLI: Err(CliError::MissingFeature{ feature: "store-sqlite", action: "solve" })
    end

    CLI-->>User: prints result or error
Loading

Class diagram for the new OrtoolsSolver and feature-gated engine facade

classDiagram
    class Solver {
        <<trait>>
        +solve(request: &SolveRequest) Result~SolveResponse, SolveError~
    }

    class SolveRequest {
    }

    class SolveResponse {
    }

    class SolveError {
        <<enum>>
        +InvalidRequest
        +other_variants
    }

    class PoiStore {
        <<trait>>
    }

    class TravelTimeProvider {
        <<trait>>
    }

    class Scorer {
        <<trait>>
    }

    class OrtoolsSolver~S,T,C~ {
        <<generic>>
        -_store: S
        -_travel_time_provider: T
        -_scorer: C
        +new(store: S, travel_time_provider: T, scorer: C) OrtoolsSolver~S,T,C~
        +solve(request: &SolveRequest) Result~SolveResponse, SolveError~
    }

    class VrpSolver {
        +new(store: SqlitePoiStore, provider: HttpTravelTimeProvider, scorer: UserRelevanceScorer) VrpSolver
        +solve(request: &SolveRequest) Result~SolveResponse, SolveError~
    }

    class SqlitePoiStore {
    }

    class HttpTravelTimeProvider {
    }

    class UserRelevanceScorer {
    }

    class wildside_engine_facade {
        <<crate facade>>
        +Diagnostics
        +InterestProfile
        +PoiStore
        +PointOfInterest
        +Route
        +SolveError
        +SolveRequest
        +SolveResponse
        +Solver
        +Theme
        +TravelTimeError
        +TravelTimeMatrix
        +TravelTimeProvider
        +SqlitePoiStore ~cfg(store-sqlite)~
        +SqlitePoiStoreError ~cfg(store-sqlite)~
        +VrpSolver ~cfg(solver-vrp)~
        +OrtoolsSolver ~cfg(solver-ortools)~
    }

    %% Relationships
    Solver <|.. OrtoolsSolver
    Solver <|.. VrpSolver
    OrtoolsSolver ..> PoiStore
    OrtoolsSolver ..> TravelTimeProvider
    OrtoolsSolver ..> Scorer
    VrpSolver ..> SqlitePoiStore
    VrpSolver ..> HttpTravelTimeProvider
    VrpSolver ..> UserRelevanceScorer
    wildside_engine_facade ..> VrpSolver
    wildside_engine_facade ..> OrtoolsSolver
    wildside_engine_facade ..> SqlitePoiStore
Loading

Flow diagram for compile-time solver selection logic

flowchart TD
    A["Start: compile wildside-cli"] --> B{feature store-sqlite?}

    B -- no --> E["DefaultSolveSolverBuilder.build returns CliError::MissingFeature{ feature: store-sqlite, action: solve }"]

    B -- yes --> C{feature solver-vrp?}

    C -- yes --> F["build_solver_with_features uses VrpSolver"]

    C -- no --> D{feature solver-ortools?}

    D -- yes --> G["build_solver_with_features uses OrtoolsSolver (placeholder)"]

    D -- no --> H["build_solver_with_features returns CliError::MissingFeature{ feature: solver-vrp or solver-ortools, action: solve }"]
Loading

File-Level Changes

Change Details Files
Wire workspace-wide feature flags through a new root engine crate and manifests, including optional solver/store crates and updated dependencies.
  • Convert the root Cargo.toml into the wildside-engine package that re-exports wildside-core and solver/store implementations behind features.
  • Define root features default, serde, solver-vrp, solver-ortools, and store-sqlite and wire them to optional dependencies using dep:-scoped features.
  • Add wildside-solver-ortools to the workspace members and as an optional dependency where needed.
  • Bump rstest-bdd and rstest-bdd-macros to 0.3.0 in the workspace dependencies.
Cargo.toml
Cargo.lock
Gate SQLite-backed POI store and spatial index helpers behind a store-sqlite feature in wildside-core, and keep PoiStore usable without SQLite.
  • Refactor wildside-core store module into store/mod.rs with a PoiStore trait and conditional submodules for sqlite and spatial_index.
  • Introduce sqlite.rs with SqlitePoiStore and SqlitePoiStoreError, backed by rusqlite and a persisted RTree index, plus comprehensive unit tests.
  • Introduce spatial_index.rs defining the WSPI on-disk format, read/write helpers, and associated errors, with round-trip and error-path tests.
  • Make rusqlite optional, add store-sqlite feature that enables rusqlite and serde stack, and conditionally re-export SqlitePoiStore and errors from lib.rs.
  • Guard test-support helpers that write SQLite artefacts behind store-sqlite, so core compiles without SQLite.
wildside-core/Cargo.toml
wildside-core/src/lib.rs
wildside-core/src/store/mod.rs
wildside-core/src/store/sqlite.rs
wildside-core/src/store/spatial_index.rs
wildside-core/src/test_support.rs
wildside-core/tests/sqlite_poi_store_behaviour.rs
Update wildside-cli to respect feature flags for SQLite ingest and solver selection, returning explicit errors when required features are missing.
  • Make wildside-cli depend on wildside-core with default-features=false and explicit serde feature, and make solvers optional with a CLI features section that forwards solver-vrp, solver-ortools, and store-sqlite.
  • Gate ingest-related imports and helpers (file IO, wikidata ETL, spatial index writing, fixtures helpers) behind store-sqlite so the crate builds without SQLite.
  • Split execute_ingest into a store-sqlite implementation and a not(store-sqlite) stub that returns CliError::MissingFeature { feature: "store-sqlite", action: "ingest" }.
  • Add a MissingFeature variant to CliError, and gate SQLite-specific error variants (OpenPoiStore, WriteSpatialIndex) behind store-sqlite.
  • Gate DefaultSolveSolverBuilder implementation for SolveSolverBuilder on store-sqlite, and introduce build_solver_with_features with cfg branches to pick VrpSolver, OrtoolsSolver, or a MissingFeature error based on solver-vrp/solver-ortools flags.
  • Add unit tests in solve.rs to assert compile-time solver selection via SOLVER_SELECTION, and CLI tests/BDD steps to verify ingest failure when store-sqlite is disabled.
wildside-cli/Cargo.toml
wildside-cli/src/lib.rs
wildside-cli/src/error.rs
wildside-cli/src/solve.rs
wildside-cli/src/tests/helpers.rs
wildside-cli/src/tests/mod.rs
wildside-cli/src/tests/feature_flags.rs
wildside-cli/src/tests/feature_flag_steps.rs
wildside-cli/tests/features/feature_flags.feature
Add an OR-Tools placeholder solver crate and expose it through the engine facade and CLI feature wiring.
  • Create wildside-solver-ortools crate that implements a generic OrtoolsSolver<S,T,C> over PoiStore, TravelTimeProvider, and Scorer, with solve() returning SolveError::InvalidRequest as a stub.
  • Expose OrtoolsSolver via cfg(feature = "solver-ortools") re-export from src/lib.rs in the root engine crate.
  • Wire wildside-solver-ortools as an optional dependency in the root package and CLI, controlled by the solver-ortools feature.
wildside-solver-ortools/Cargo.toml
wildside-solver-ortools/src/lib.rs
src/lib.rs
Cargo.toml
Update documentation and roadmap to describe the new feature-flag scheme and provide a concrete execution plan for Phase 4.
  • Add docs/execplans/phase-4-feature-flags.md describing goals, plan of work, decisions, validation, and commands for feature-flag implementation and testing.
  • Update docs/wildside-engine-design.md feature flags section to list solver-vrp, solver-ortools, store-sqlite, and their default behaviors, including serde, and describe future optional features.
  • Clarify in docs/users-guide.md that SqlitePoiStore is available only when store-sqlite is enabled and adjust wording around its errors.
  • Mark roadmap items for defining root features, forwarding them via dep:-scoped entries, and using cfg(feature) for solver/store implementations as completed, and adjust OR-Tools item to use solver-ortools feature name.
docs/execplans/phase-4-feature-flags.md
docs/wildside-engine-design.md
docs/users-guide.md
docs/roadmap.md

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

- Introduce a new root package `wildside-engine` with optional features:
  - `solver-vrp` (default)
  - `solver-ortools` (optional stub solver crate added)
  - `store-sqlite` (optional SQLite POI store)
- Gate `SqlitePoiStore` and solver usage behind feature flags in `wildside-core` and `wildside-cli`
- Forward features from member crates via `dep:` dependencies for a single source of truth
- Add CLI errors and tests for missing features to handle no-default-features builds gracefully
- Remove monolithic `wildside-core::store` module; split into submodules under feature flag
- Introduce tests and behavioural scenarios exercising feature selection and fallback
- Update documentation and roadmap to reflect feature flag strategy and implementation

This enables flexible builds and prepares for future expansion of solver and store backends.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Add Phase 4 Feature Flags ExecPlan doc Implement Phase 4 feature flags with tests and deployment Jan 2, 2026
@leynos leynos marked this pull request as ready for review January 2, 2026 06:17
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 5 issues, and left some high level feedback:

  • The multiple build_solver_with_features implementations in wildside-cli/src/solve.rs rely on a growing set of #[cfg] combinations; consider centralising solver selection (e.g. with a small enum or cfg_if! block) so adding a new solver or changing priority only touches one place.
  • In wildside-core, all spatial index helpers and SQLite store types are gated behind store-sqlite, but the module-level visibility is still public; if these are not intended as part of the stable public API for non-SQLite builds, consider tightening visibility (e.g. pub(crate) where possible) to avoid accidental external coupling.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The multiple `build_solver_with_features` implementations in `wildside-cli/src/solve.rs` rely on a growing set of `#[cfg]` combinations; consider centralising solver selection (e.g. with a small enum or `cfg_if!` block) so adding a new solver or changing priority only touches one place.
- In `wildside-core`, all spatial index helpers and SQLite store types are gated behind `store-sqlite`, but the module-level visibility is still public; if these are not intended as part of the stable public API for non-SQLite builds, consider tightening visibility (e.g. `pub(crate)` where possible) to avoid accidental external coupling.

## Individual Comments

### Comment 1
<location> `wildside-cli/src/solve.rs:23` </location>
<code_context>
+    not(feature = "solver-vrp")
+))]
+use wildside_solver_ortools::OrtoolsSolver;
+#[cfg(all(feature = "store-sqlite", feature = "solver-vrp"))]
 use wildside_solver_vrp::VrpSolver;

</code_context>

<issue_to_address>
**issue (complexity):** Consider centralizing the feature-flag-based solver selection and dependency setup into a few helpers/types so the cfg logic is no longer duplicated across multiple functions, impls, and tests.

You can keep the feature behavior but collapse most of the `cfg` surface by:

1. Moving solver selection to a type alias (or small helper).
2. Removing the split `impl` for `SolveSolverBuilder`.
3. Reusing the same selection logic in tests instead of mirroring cfgs.

### 1. Collapse the three `build_solver_with_features` fns

Instead of three cfg‑gated fns with identical signatures, define a type alias for the selected solver and keep a single construction site:

```rust
#[cfg(all(feature = "store-sqlite", feature = "solver-vrp"))]
type SelectedSolver = VrpSolver;

#[cfg(all(
    feature = "store-sqlite",
    not(feature = "solver-vrp"),
    feature = "solver-ortools"
))]
type SelectedSolver = OrtoolsSolver;
```

Then one function handles all runtime construction:

```rust
#[cfg(feature = "store-sqlite")]
fn build_solver_with_features(
    store: SqlitePoiStore,
    provider: HttpTravelTimeProvider,
    scorer: UserRelevanceScorer,
) -> Result<Box<dyn Solver>, CliError> {
    #[cfg(any(feature = "solver-vrp", feature = "solver-ortools"))]
    {
        Ok(Box::new(SelectedSolver::new(store, provider, scorer)))
    }

    #[cfg(all(not(feature = "solver-vrp"), not(feature = "solver-ortools")))]
    {
        // prevent unused warnings
        let _ = (store, provider, scorer);
        Err(CliError::MissingFeature {
            feature: "solver-vrp or solver-ortools",
            action: "solve",
        })
    }
}
```

This keeps the combinatorial feature logic in one place (the type alias) and removes duplicated signatures and multiple cfg blocks on the same function name.

### 2. Avoid splitting the `SolveSolverBuilder` impl

You can hide the `store-sqlite` dependency behind a small helper and keep a single impl for `SolveSolverBuilder`:

```rust
fn make_store_and_deps(
    config: &SolveConfig,
) -> Result<(SqlitePoiStore, HttpTravelTimeProvider, UserRelevanceScorer), CliError> {
    #[cfg(feature = "store-sqlite")]
    {
        let store = SqlitePoiStore::open(
            config.pois_db.as_std_path(),
            config.spatial_index.as_std_path(),
        )?;
        let scorer =
            UserRelevanceScorer::with_defaults(&config.pois_db, &config.popularity)?;
        let provider = HttpTravelTimeProvider::new(config.osrm_base_url.clone())
            .map_err(|source| CliError::BuildTravelTimeProvider {
                base_url: config.osrm_base_url.clone(),
                source,
            })?;
        Ok((store, provider, scorer))
    }

    #[cfg(not(feature = "store-sqlite"))]
    {
        let _ = config;
        Err(CliError::MissingFeature {
            feature: "store-sqlite",
            action: "solve",
        })
    }
}
```

Then the impl becomes cfg‑free and easier to audit:

```rust
impl SolveSolverBuilder for DefaultSolveSolverBuilder {
    fn build(&self, config: &SolveConfig) -> Result<Box<dyn Solver>, CliError> {
        let (store, provider, scorer) = make_store_and_deps(config)?;
        build_solver_with_features(store, provider, scorer)
    }
}
```

Now all feature logic is localized in `SelectedSolver`/`build_solver_with_features` and `make_store_and_deps`, not spread over multiple impl blocks.

### 3. Reuse selection logic in tests

Instead of mirroring the solver selection cfgs in a `SOLVER_SELECTION` const and three separate test fns, you can test the same logic used in production (or at least the same `SelectedSolver` cfg):

```rust
#[cfg(test)]
mod feature_flag_tests {
    use super::*;

    #[test]
    fn solver_selection_matches_features() {
        #[cfg(feature = "solver-vrp")]
        assert_eq!(std::any::type_name::<SelectedSolver>(), std::any::type_name::<VrpSolver>());

        #[cfg(all(not(feature = "solver-vrp"), feature = "solver-ortools"))]
        assert_eq!(std::any::type_name::<SelectedSolver>(), std::any::type_name::<OrtoolsSolver>());

        #[cfg(all(not(feature = "solver-vrp"), not(feature = "solver-ortools")))]
        {
            // here you can assert that building fails with MissingFeature
            let builder = DefaultSolveSolverBuilder;
            let cfg = SolveConfig::default();
            let err = builder.build(&cfg).unwrap_err();
            // assert on err if desired
        }
    }
}
```

This removes a separate “cfg maze” in tests and directly verifies the same feature selection surface that production uses.
</issue_to_address>

### Comment 2
<location> `wildside-solver-ortools/src/lib.rs:14` </location>
<code_context>
+
+/// Placeholder solver for the optional OR-Tools backend.
+#[derive(Debug)]
+pub struct OrtoolsSolver<S, T, C>
+where
+    S: PoiStore,
</code_context>

<issue_to_address>
**issue (review_instructions):** Add unit and behavioural tests for the new `solver-ortools` feature and `OrtoolsSolver` implementation.

Add unit tests that exercise `OrtoolsSolver::new` and `solve`, asserting that it returns the documented placeholder `SolveError::InvalidRequest` and that the type integrates correctly with the `Solver` trait. In addition, add behavioural tests (for example using `rstest-bdd`) that run the engine/CLI with the `solver-ortools` feature enabled to verify end-to-end selection and error behaviour for this solver. The new `solver-ortools` feature must not ship without both unit and behavioural coverage.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural and unit tests are required.

</details>
</issue_to_address>

### Comment 3
<location> `wildside-cli/src/error.rs:37` </location>
<code_context>
     },
+    /// The requested operation requires a missing compile-time feature.
+    #[error("{action} requires the `{feature}` feature to be enabled")]
+    MissingFeature {
+        feature: &'static str,
+        action: &'static str,
</code_context>

<issue_to_address>
**issue (review_instructions):** Add tests that cover the new `MissingFeature` CLI error for disabled solver/store feature paths.

Add both unit and behavioural tests that exercise the new `CliError::MissingFeature` paths for operations such as `ingest` and `solve` when `store-sqlite`, `solver-vrp`, or `solver-ortools` are disabled. For example, add rstest-bdd scenarios in the new `feature_flags` test files that invoke the CLI under different feature combinations and assert that the user-facing output reflects `MissingFeature` with the correct `feature` and `action` values. Ensure unit tests also validate the error formatting and mapping from configuration/feature state to this error.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural and unit tests are required.

</details>
</issue_to_address>

### Comment 4
<location> `wildside-cli/src/solve.rs:191` </location>
<code_context>
+}
+
+#[cfg(all(feature = "store-sqlite", feature = "solver-vrp"))]
+fn build_solver_with_features(
+    store: SqlitePoiStore,
+    provider: HttpTravelTimeProvider,
</code_context>

<issue_to_address>
**issue (review_instructions):** Add behavioural tests for the new feature-flag-based solver selection logic in `build_solver_with_features`.

Keep the unit tests in `feature_flag_tests`, but add behavioural tests that verify end-to-end solver selection via the CLI for the various feature combinations (`solver-vrp` only, `solver-ortools` only, both, and neither). Implement rstest-bdd scenarios that run `run_solve` (or the public CLI entry point) under each combination and assert which solver implementation is used or that an appropriate `MissingFeature` error is produced. Ensure these behavioural tests live in the new `feature_flags` BDD files instead of leaving them effectively empty.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural and unit tests are required.

</details>
</issue_to_address>

### Comment 5
<location> `docs/users-guide.md:74` </location>
<code_context>
-JSON tag payloads, and I/O or SQLite errors.[^8]
+store is `SqlitePoiStore`, which is available when the `store-sqlite` feature
+is enabled. It opens two artefacts: a read-only SQLite database and a
+serialised R\*-tree. The loader verifies both files by reading a `WSPI` magic
+header, checking the format version (`2`), and ensuring that every indexed
+point exists in the database. Failing checks raise `SqlitePoiStoreError`,
</code_context>

<issue_to_address>
**suggestion (review_instructions):** "serialised" should use -ize spelling ("serialized") to match the specified en-GB-oxendic convention.

The style guide here explicitly calls for -ize spellings. Please change "serialised" to "serialized" in this sentence.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Use en-GB-oxendic (-ize / -yse / -our) spelling and grammar.

</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread wildside-cli/src/solve.rs
Comment thread docs/users-guide.md Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

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

⚠️ Outside diff range comments (1)
docs/wildside-engine-design.md (1)

1-1124: File exceeds maximum length guideline.

The file contains 1124 lines, which exceeds the 400-line maximum specified in the coding guidelines. While the PR's changes (lines 921-939) are localized and appropriate, the file length violates the established limit and should be addressed.

The coding guidelines state: "Files must not exceed 400 lines in length. Large modules must be decomposed."

Given the scope of this document, decomposition options include:

  • Split by major sections (e.g., separate files for data ingestion, solver implementation, testing strategy)
  • Extract implementation details into separate design documents
  • Move works cited and detailed comparisons to appendices

Do you want me to generate a proposed decomposition structure showing how this design document could be split into multiple files whilst maintaining cross-references and narrative flow?

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 881a319 and f37fcc6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (27)
  • Cargo.toml
  • docs/execplans/phase-4-feature-flags.md
  • docs/roadmap.md
  • docs/users-guide.md
  • docs/wildside-engine-design.md
  • src/lib.rs
  • wildside-cli/Cargo.toml
  • wildside-cli/src/error.rs
  • wildside-cli/src/lib.rs
  • wildside-cli/src/solve.rs
  • wildside-cli/src/tests/feature_flag_steps.rs
  • wildside-cli/src/tests/feature_flags.rs
  • wildside-cli/src/tests/helpers.rs
  • wildside-cli/src/tests/mod.rs
  • wildside-cli/src/tests/pipeline.rs
  • wildside-cli/src/tests/pipeline_steps.rs
  • wildside-cli/tests/features/feature_flags.feature
  • wildside-core/Cargo.toml
  • wildside-core/src/lib.rs
  • wildside-core/src/store.rs
  • wildside-core/src/store/mod.rs
  • wildside-core/src/store/spatial_index.rs
  • wildside-core/src/store/sqlite.rs
  • wildside-core/src/test_support.rs
  • wildside-core/tests/sqlite_poi_store_behaviour.rs
  • wildside-solver-ortools/Cargo.toml
  • wildside-solver-ortools/src/lib.rs
💤 Files with no reviewable changes (1)
  • wildside-core/src/store.rs
🧰 Additional context used
📓 Path-based instructions (6)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make check-fmt, make lint, and make test before committing in Rust projects.
make check-fmt executes cargo fmt --workspace -- --check to validate formatting across the entire workspace without modifying files.
make lint executes cargo clippy --workspace --all-targets --all-features -- -D warnings to lint every target with all features enabled and deny all Clippy warnings.
make test executes cargo test --workspace to run the full workspace test suite.
Clippy warnings MUST be disallowed in Rust code.
Fix any warnings emitted during tests in the Rust code itself rather than silencing them.
Where a Rust function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a Rust function has too many parameters, group related parameters in meaningfully named structs.
Where a Rust function is returning a large error, consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality in Rust. Run both before and after making any change.
Every Rust module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings in Rust.
Handle errors with the Result type instead of panicking in Rust where feasible.
Avoid unsafe code in Rust unless absolutely necessary, and document any usage clearly.
Place function attributes after doc comments in Rust.
Do not use return in single-line Rust functions.
Use predicate functions for conditional criteria in Rust with more than two branches.
Rust lints must not be silenced except as a last resort.
Rust lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow in Rust.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated Rust tests with `#[rstest(...)...

Files:

  • wildside-cli/src/tests/mod.rs
  • wildside-cli/src/tests/feature_flags.rs
  • wildside-core/tests/sqlite_poi_store_behaviour.rs
  • wildside-core/src/lib.rs
  • wildside-cli/src/tests/helpers.rs
  • wildside-solver-ortools/src/lib.rs
  • wildside-core/src/store/mod.rs
  • wildside-cli/src/error.rs
  • wildside-cli/src/tests/feature_flag_steps.rs
  • wildside-cli/src/tests/pipeline_steps.rs
  • wildside-core/src/store/sqlite.rs
  • wildside-cli/src/tests/pipeline.rs
  • wildside-core/src/store/spatial_index.rs
  • wildside-cli/src/lib.rs
  • wildside-core/src/test_support.rs
  • wildside-cli/src/solve.rs
  • src/lib.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • wildside-cli/src/tests/mod.rs
  • wildside-cli/src/tests/feature_flags.rs
  • wildside-core/tests/sqlite_poi_store_behaviour.rs
  • wildside-core/src/lib.rs
  • wildside-cli/src/tests/helpers.rs
  • wildside-solver-ortools/src/lib.rs
  • wildside-core/src/store/mod.rs
  • wildside-cli/src/error.rs
  • wildside-cli/src/tests/feature_flag_steps.rs
  • wildside-cli/src/tests/pipeline_steps.rs
  • wildside-core/src/store/sqlite.rs
  • wildside-cli/src/tests/pipeline.rs
  • wildside-core/src/store/spatial_index.rs
  • wildside-cli/src/lib.rs
  • wildside-core/src/test_support.rs
  • wildside-cli/src/solve.rs
  • src/lib.rs
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
When new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve, proactively update the relevant file(s) in the docs/ directory to reflect the latest state. Ensure the documentation remains accurate and current.
Documentation must use en-GB-oxendict spelling and grammar ("-ize" / "-yse" / "-our") with the exception of the LICENSE filename.

docs/**/*.md: Use British English based on the Oxford English Dictionary locale en-GB, including -ize suffixes (e.g., realize, organization), -lyse suffixes (e.g., analyse, paralyse), -our suffixes (e.g., colour, behaviour), -re suffixes (e.g., centre, calibre), double 'l' (e.g., cancelled, counsellor), maintained 'e' (e.g., likeable, rateable), -ogue suffixes (e.g., analogue, catalogue)
Use the Oxford comma in documentation: 'ships, planes, and hovercraft' where it aids comprehension
Treat company names as collective nouns, for example: 'Concordat Industries are expanding'
Write headings in sentence case
Use Markdown headings (#, ##, ###, and so on) in order without skipping levels
Follow markdownlint recommendations
Provide code blocks and lists using standard Markdown syntax
Always provide a language identifier for fenced code blocks; use 'plaintext' for non-code text
Use '-' as the first level bullet and renumber lists when items change
Prefer inline links using 'text' or angle brackets around the URL
Ensure blank lines before and after bulleted lists and fenced blocks
Ensure tables have a delimiter line below the header row
Expand any uncommon acronym on first use, for example, Continuous Integration (CI)
Wrap paragraphs at 80 columns
Do not wrap tables
Use GitHub-flavoured numeric footnotes referenced as '[^1]'
Footnotes must be numbered in order of appearance in the document
Caption ev...

Files:

  • docs/roadmap.md
  • docs/users-guide.md
  • docs/wildside-engine-design.md
  • docs/execplans/phase-4-feature-flags.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Markdown tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/roadmap.md
  • docs/users-guide.md
  • docs/wildside-engine-design.md
  • docs/execplans/phase-4-feature-flags.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/roadmap.md
  • docs/users-guide.md
  • docs/wildside-engine-design.md
  • docs/execplans/phase-4-feature-flags.md
docs/**/!(README).md

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Avoid first and second person personal pronouns outside the README.md file

Files:

  • docs/roadmap.md
  • docs/users-guide.md
  • docs/wildside-engine-design.md
  • docs/execplans/phase-4-feature-flags.md
docs/**/*.{md,rs}

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Wrap code at 120 columns

Files:

  • docs/roadmap.md
  • docs/users-guide.md
  • docs/wildside-engine-design.md
  • docs/execplans/phase-4-feature-flags.md
Cargo.toml

📄 CodeRabbit inference engine (AGENTS.md)

Cargo.toml: Use explicit version ranges in Cargo.toml and keep dependencies up-to-date.
All crate versions specified in Cargo.toml must use SemVer-compatible caret requirements (e.g., some-crate = "1.2.3"). Wildcard (*) or open-ended inequality (>=) version requirements are strictly forbidden.
Tilde requirements (~) in Rust dependencies should only be used where a dependency must be locked to patch-level updates for a specific, documented reason.

Files:

  • Cargo.toml
🧠 Learnings (1)
📚 Learning: 2026-01-02T03:31:47.671Z
Learnt from: CR
Repo: leynos/wildside-engine PR: 0
File: docs/roadmap.md:0-0
Timestamp: 2026-01-02T03:31:47.671Z
Learning: Applies to docs/wildside-solver-ortools/src/**/*.rs : Create a `wildside-solver-ortools` crate (conditionally compiled via the `ortools` feature flag) implementing the `Solver` trait using OR-Tools CP-SAT solver for the Orienteering Problem

Applied to files:

  • docs/roadmap.md
🧬 Code graph analysis (7)
wildside-cli/src/tests/feature_flags.rs (3)
wildside-cli/src/tests/helpers.rs (2)
  • write_utf8 (14-18)
  • new (44-74)
wildside-cli/src/tests/feature_flag_steps.rs (2)
  • ingest_requires_store_sqlite (103-105)
  • new (23-35)
wildside-cli/src/lib.rs (1)
  • run_ingest (63-66)
wildside-cli/src/tests/feature_flag_steps.rs (2)
wildside-cli/src/lib.rs (1)
  • run_ingest (63-66)
wildside-cli/src/tests/feature_flags.rs (1)
  • ingest_requires_store_sqlite (13-36)
wildside-core/src/store/sqlite.rs (3)
wildside-core/src/store/spatial_index.rs (2)
  • load_index_entries (132-169)
  • poi (181-187)
wildside-core/tests/sqlite_poi_store_behaviour.rs (2)
  • bbox (78-80)
  • new (29-38)
wildside-core/src/poi.rs (1)
  • envelope (46-48)
wildside-core/src/store/spatial_index.rs (1)
wildside-core/src/store/sqlite.rs (2)
  • open (73-95)
  • poi (206-212)
wildside-cli/src/lib.rs (5)
wildside-core/src/store/spatial_index.rs (1)
  • write_spatial_index (99-104)
wildside-data/src/wikidata/etl/mod.rs (1)
  • extract_linked_entity_claims (153-198)
wildside-data/src/wikidata/store/persistence.rs (1)
  • persist_claims_to_path (285-295)
wildside-data/src/ingest/mod.rs (1)
  • ingest_osm_pbf_report (149-202)
wildside-data/src/ingest/sqlite.rs (1)
  • persist_pois_to_sqlite (97-122)
wildside-core/src/test_support.rs (2)
wildside-core/tests/poi_store_behaviour.rs (1)
  • store (19-22)
wildside-core/src/store/spatial_index.rs (1)
  • write_spatial_index (99-104)
wildside-cli/src/solve.rs (2)
wildside-fs/src/lib.rs (1)
  • open_utf8_file (10-12)
wildside-solver-ortools/src/lib.rs (1)
  • new (32-38)
🪛 LanguageTool
docs/roadmap.md

[style] ~200-~200: Consider using the typographical ellipsis character here instead.
Context: ...side-data/sqlite"] ``` - [x] Use #[cfg(feature = "...")] attributes to conditionally compile...

(ELLIPSIS)

docs/users-guide.md

[style] ~73-~73: Would you like to use the Oxford spelling “serialized”? The spelling ‘serialised’ is also correct.
Context: ...acts: a read-only SQLite database and a serialised R*-tree. The loader verifies both file...

(OXFORD_SPELLING_Z_NOT_S)

docs/wildside-engine-design.md

[uncategorized] ~921-~921: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...e Flags:** The engine uses feature flags so consumers can select the solver and s...

(COMMA_COMPOUND_SENTENCE_2)


[uncategorized] ~925-~925: Loose punctuation mark.
Context: ...["solver-vrp", "store-sqlite", "serde"]`: The default build includes the nati...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~929-~929: Loose punctuation mark.
Context: ...equest/response types. - solver-vrp: Enables the native Rust solver backed b...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~932-~932: Loose punctuation mark.
Context: ...tures are enabled. - solver-ortools: Enables the optional OR-Tools solver ba...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~935-~935: Loose punctuation mark.
Context: ...T integration lands. - store-sqlite: Enables the SQLite-backed POI store and...

(UNLIKELY_OPENING_PUNCTUATION)

docs/execplans/phase-4-feature-flags.md

[uncategorized] ~13-~13: The hyphen in fully-tested is redundant.
Context: ...le keeping the default build stable and fully-tested. Success is observable when the workspa...

(ADVERB_LY_HYPHEN_FIX)


[uncategorized] ~62-~62: Possible missing comma found.
Context: ...RP solver when both solver features are enabled and ship a placeholder OR-Tools solve...

(AI_HYDRA_LEO_MISSING_COMMA)


[misspelling] ~84-~84: This word is normally spelled with a hyphen.
Context: ...ted in wildside-core/src/store.rs and re- exported from wildside-core/src/lib.rs. The ro...

(EN_COMPOUNDS_RE_EXPORTED)


[style] ~87-~87: Consider using the typographical ellipsis character here instead.
Context: ...lite, with conditional compilation via #[cfg(feature = "...")]. Testing guidance is in docs/rust...

(ELLIPSIS)


[style] ~122-~122: Consider using the typographical ellipsis character here instead.
Context: ...y re-export the SQLite store and to add doc(cfg(...)) hints where appropriate. Adjust exis...

(ELLIPSIS)


[style] ~125-~125: Consider using a more formal replacement.
Context: ... compiled when the feature is enabled. After that, update wildside-cli to respect the f...

(AFTER_THAT)


[style] ~141-~141: Consider using the typographical ellipsis character here instead.
Context: ...and scenarios are guarded with the same #[cfg(feature = "...")] selectors so they only compile unde...

(ELLIPSIS)


[style] ~183-~183: Consider removing “of” to be more concise
Context: ...ceptance The change is acceptable when all of the following are true: - make check-fmt...

(ALL_OF_THE)


[uncategorized] ~185-~185: Loose punctuation mark.
Context: ... following are true: - make check-fmt, make lint, and make test succeed wi...

(UNLIKELY_OPENING_PUNCTUATION)


[style] ~198-~198: Consider using the typographical ellipsis character here instead.
Context: ... that the module is behind the intended #[cfg(feature = "...")] guard and that its dependencies are...

(ELLIPSIS)


[uncategorized] ~216-~216: Loose punctuation mark.
Context: ...nifest with these names: - solver-vrp: enables wildside-solver-vrp. - `solve...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~217-~217: Loose punctuation mark.
Context: ...wildside-solver-vrp. - solver-ortools: enables wildside-solver-ortools. - s...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~218-~218: Loose punctuation mark.
Context: ...ldside-solver-ortools. - store-sqlite`: enables the SQLite-backed POI store. T...

(UNLIKELY_OPENING_PUNCTUATION)

🔍 Remote MCP

Let me search for more specific information about rstest-bdd testing patterns and any potential issues with feature-gated tests.

Summary of Relevant Context for PR Review

Based on my search for additional context, here's what would be helpful for reviewing this Phase 4 feature flags PR:

Rust Feature Flags Best Practices

Features are defined in the [features] table in Cargo.toml, and each feature specifies an array of other features or optional dependencies that it enables. The PR implementation aligns with standard patterns:

  • Code can conditionally include support at compile time using #[cfg(feature = "name")] attributes, with Cargo setting features using the rustc --cfg flag.
  • Dependencies automatically enable default features unless default-features = false is specified, which can make it difficult to ensure defaults aren't enabled, especially when dependencies appear multiple times in the dependency graph, so every package must ensure that default-features = false is specified to avoid enabling them.

Review Note: The PR's approach of using default-features = false in wildside-cli for wildside-core is appropriate here.

API Design Considerations

You should avoid feature-gating methods on public traits, as external implementors won't know whether to implement feature-gated methods or not.

Review Point: The PR avoids this by keeping the PoiStore trait public and unconditional while gating only the SQLite-specific implementations (SqlitePoiStore, SqlitePoiStoreError).

Feature Combinatorics

If your crate has N independent features, then all of the 2^N possible build combinations can occur in practice, so your CI system should cover all of these combinations in all available test variants.

Critical Review Focus: With 3 independent features (solver-vrp, solver-ortools, store-sqlite), the PR creates 2³=8 possible build combinations. CI should validate these systematically.

Testing Framework (rstest-bdd)

rstest-bdd keeps unit and acceptance tests on the same runner (cargo test), offers first-class fixtures shared between test types, and provides ergonomic step definitions with #[given], #[when], #[then] with typed placeholders.

.feature files are processed at compile time as static scenarios, and step definitions must be known at compile time (no dynamic registration).

Review Note: The PR correctly uses #[cfg(feature = ...)] on test modules like feature_flags.rs and pipeline.rs since rstest-bdd scenarios are compile-time resolved. Test files that reference features unavailable in a given build must be conditionally compiled themselves.

Workspace Dependency Management

The PR follows recommended patterns by using workspace dependency tables where dependencies are inherited by specifying them in [workspace.dependencies], then adding them to [dependencies] with workspace = true, with the caveat that the [workspace.dependencies] table itself is not allowed to specify optional.


Key Review Considerations

  1. Feature Matrix Coverage: Verify CI tests all 8 feature combinations (default, --no-default-features, individual features, and combinations).
  2. Trait Stability: The unconditional PoiStore trait and conditional implementations provide good API stability.
  3. Compile-Time Gating: Test module gating with #[cfg] is necessary for rstest-bdd scenarios to compile correctly.
  4. Optional Dependencies: Confirm wildside-solver-ortools and solver implementations are properly marked optional in Cargo.toml files.

[::web_search::]

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: Sourcery review
🔇 Additional comments (66)
docs/roadmap.md (4)

181-182: LGTM: Checklist item accurately reflects completion.

The feature flag naming ("solver-ortools") is consistent with the implementation across workspace manifests.


183-184: LGTM: Feature forwarding implementation is complete.

The checklist accurately reflects that feature flags are forwarded from member crates through the root manifest.


200-201: LGTM: Conditional compilation is correctly marked complete.

The implementation uses #[cfg(feature = "...")] attributes as required.


212-213: LGTM: Feature flag naming is consistent.

The update from "ortools" to "solver-ortools" aligns with the actual feature names defined in the workspace manifests.

wildside-core/Cargo.toml (3)

9-9: LGTM: Correct feature gating for rstar.

The rstar crate remains a required dependency while its serde feature is conditionally activated through line 25, which is the appropriate pattern.


10-10: LGTM: SQLite dependency correctly gated.

The optional flag and bundled feature are appropriate for feature-gated storage.


14-14: Bincode is actively used in wildside-core/src/store/sqlite.rs and wildside-core/src/store/spatial_index.rs for serialisation. The dependency configuration is correct and necessary.

docs/execplans/phase-4-feature-flags.md (7)

10-17: LGTM: Purpose is clearly stated with measurable success criteria.

The section clearly articulates the goals, scope, and observable success criteria for the feature flag implementation.


19-36: LGTM: Progress tracking is comprehensive and timestamped.

The checklist provides a clear record of completed work with ISO 8601 timestamps.


37-43: LGTM: Valuable documentation of workspace dependency constraints.

This discovery explains the path dependency strategy used in wildside-cli/Cargo.toml line 20.


95-148: LGTM: Implementation plan is comprehensive and actionable.

The plan provides clear, step-by-step guidance for implementing feature flags across the workspace with appropriate gating strategies.


150-180: LGTM: Concrete steps provide clear execution commands.

The shell commands are well-structured with appropriate log capture for validation tracking.


181-193: LGTM: Validation criteria are clear and measurable.

The acceptance criteria provide specific, testable conditions for success with appropriate feature combinations.


212-229: LGTM: Interface specifications are precise and actionable.

The required feature names and public API surface are clearly documented with code examples.

wildside-cli/Cargo.toml (2)

20-20: Path dependency correctly works around workspace default-features constraint.

This change implements the decision documented in docs/execplans/phase-4-feature-flags.md lines 51-54. The path override is necessary because workspace dependency inheritance forbids default-features = false.

Based on learnings, the wildside-cli crate needs independent control over which wildside-core features are enabled, specifically to disable store-sqlite by default and enable it only through the CLI's own feature flags.


23-24: LGTM: Solver dependencies correctly marked optional.

Both VRP and OR-Tools solvers are properly gated behind their respective features.

wildside-solver-ortools/Cargo.toml (1)

1-14: Manifest structure and placeholder implementation are correct.

The package metadata and dependency declarations follow workspace conventions appropriately. The crate is correctly marked as unpublished. The OrtoolsSolver struct properly implements the Solver trait and returns SolveError::InvalidRequest as specified in the execution plan, reserving the API surface for future OR-Tools integration without pulling native dependencies.

Cargo.toml (2)

1-21: Feature flag structure is well-designed.

The root package structure correctly implements feature flags with appropriate dependency gating. Using default-features = false for wildside-core at line 11 is correct—it prevents unintended feature activation when the crate appears multiple times in the dependency graph.

The store-sqlite feature correctly depends on serde at line 20, ensuring serialisation support is available when the SQLite store is enabled.

Verify that CI tests all 8 feature combinations (2³ from solver-vrp, solver-ortools, store-sqlite). Per Rust best practices, all possible build combinations should be validated.

#!/bin/bash
# Description: Check if CI workflows test multiple feature combinations

# Search for feature flag testing patterns in CI configuration
fd -e yml -e yaml . .github/workflows | xargs rg -n "all-features|no-default-features|features"

53-54: Verify rstest-bdd 0.3.0 compatibility.

The upgrade from rstest-bdd 0.2.0 to 0.3.0 may introduce breaking changes affecting test scenarios. Confirm that all existing BDD tests compile and pass with this version.

Search the web for rstest-bdd 0.3.0 release notes to identify any breaking changes:

rstest-bdd 0.3.0 release notes breaking changes
wildside-cli/src/tests/helpers.rs (1)

4-5: Feature gating is correctly applied to test helpers.

All new store-sqlite-dependent helpers are consistently gated behind #[cfg(feature = "store-sqlite")]. This includes the base64 import (lines 4-5), read_utf8 (lines 20-24), and the fixture helpers (lines 135-165).

The use of .expect() throughout these functions is acceptable in test code as per the coding guidelines.

Also applies to: 20-24, 135-165

wildside-cli/src/tests/mod.rs (1)

9-10: Module declarations are correctly ungated.

The new modules feature_flag_steps and feature_flags are declared without feature gates at the module level. This is appropriate if these modules test behaviour when features are DISABLED (testing MissingFeature error paths). The modules themselves must handle any necessary internal feature gating.

Verify that feature_flag_steps.rs and feature_flags.rs apply appropriate #[cfg] gating internally for any feature-dependent test code:

#!/bin/bash
# Description: Check feature gating in new test modules

# Look for cfg attributes in the new test modules
rg -n "cfg\(feature" wildside-cli/src/tests/feature_flag*.rs
wildside-cli/src/tests/pipeline.rs (1)

3-4: Module-level feature gating is correctly applied.

Adding #![cfg(feature = "store-sqlite")] at line 3 appropriately gates the entire test module, which depends on SqlitePoiStore throughout. This prevents compilation errors when the feature is disabled and aligns with rstest-bdd's compile-time scenario resolution requirements.

The blank line at line 4 improves readability between the attribute and imports.

wildside-cli/src/tests/pipeline_steps.rs (1)

3-4: Feature gating is consistent and correct for rstest-bdd scenarios.

The module-level #![cfg(feature = "store-sqlite")] at line 3 correctly gates rstest-bdd scenarios that depend on SqlitePoiStore. This matches the gating approach in pipeline.rs and is necessary because rstest-bdd processes scenarios at compile time—the entire module must be excluded when the feature is unavailable.

wildside-core/tests/sqlite_poi_store_behaviour.rs (1)

3-4: Feature gating correctly applied for rstest-bdd scenarios.

The module-level #[cfg(feature = "store-sqlite")] attribute ensures the test file compiles only when the feature is enabled. This aligns with rstest-bdd's compile-time scenario resolution and avoids build failures when the feature is disabled.

wildside-cli/tests/features/feature_flags.feature (1)

1-6: BDD scenario correctly defines feature-gating behaviour.

The scenario structure follows proper Gherkin syntax and clearly documents the expected behaviour when the store-sqlite feature is disabled. The steps map to implementations in the feature_flag_steps module.

docs/wildside-engine-design.md (1)

921-939: Feature flag documentation accurately reflects implementation.

The feature flag section correctly documents:

  • Default features (solver-vrp, store-sqlite, serde)
  • Individual feature purposes and behaviour
  • Preference handling for multiple solver features
  • Future feature roadmap

The content is well-structured and provides clear guidance for consumers selecting feature combinations.

wildside-core/src/lib.rs (1)

21-23: LGTM!

The feature-gated re-exports are correctly structured. PoiStore remains unconditionally available whilst SqlitePoiStore and SqlitePoiStoreError are appropriately gated behind store-sqlite. This aligns with the API design guidance to avoid feature-gating public trait methods whilst gating concrete implementations.

wildside-cli/src/error.rs (2)

35-40: LGTM!

The MissingFeature variant is well-designed with clear, actionable error messaging. Keeping it unconditionally compiled ensures graceful degradation when features are disabled.


92-98: Feature gating is correctly applied.

The WriteSpatialIndex and OpenPoiStore variants are appropriately gated behind store-sqlite, ensuring the error enum compiles cleanly regardless of feature configuration.

Also applies to: 121-123

wildside-core/src/store/mod.rs (3)

1-10: LGTM!

Module documentation clearly explains purpose and utility. The feature-gated submodule structure is well-organised.


21-75: Well-documented trait with comprehensive examples.

The PoiStore trait documentation is thorough, including coordinate system conventions, antimeridian caveats, and a working example. The trait remains unconditionally available whilst concrete implementations are feature-gated—this is the correct approach.


77-126: Excellent parameterised test coverage.

The boundary condition tests using #[rstest(...)] parameterised cases are well-structured, covering edges, corners, and just-outside scenarios. This aligns with the coding guidelines for using rstest fixtures and parameterised cases.

src/lib.rs (3)

1-6: LGTM!

Module documentation is clear and #![forbid(unsafe_code)] appropriately enforces safety at the crate level.


13-20: Feature-gated re-exports are correctly structured.

The conditional re-exports for SqlitePoiStore, VrpSolver, and OrtoolsSolver follow the established pattern. Consumers can depend on the facade and selectively enable backends via feature flags.


8-11: Add missing re-exports from wildside_core.

The facade omits Scorer and SolveRequestValidationError, both of which are exported by wildside_core. Include these in the re-export block at lines 8–11 alongside the existing types.

wildside-solver-ortools/src/lib.rs (1)

1-7: LGTM!

Module documentation clearly states this is a placeholder and #![forbid(unsafe_code)] is appropriate.

wildside-core/src/store/spatial_index.rs (4)

1-5: LGTM!

Module documentation clearly explains the purpose and relationship to SQLite-backed POI store.


32-69: Well-structured error types.

Error variants are comprehensive with clear messages and proper source attribution. The distinction between read errors (SpatialIndexError) and write errors (SpatialIndexWriteError) is appropriate.


181-199: Test fixtures use .expect() appropriately.

The use of .expect() in test fixtures is acceptable per coding guidelines (allow-expect-in-tests = true). The messages are clear.


283-295: Remove this test or expand it with additional assertions.

The test write_index_persists_spatial_index_file adds no value—load_index_entries_round_trips_entries already verifies round-trip serialisation and format compatibility. Both tests call the same write_index function and validate the same data structure. Either consolidate these checks or remove the redundant test.

wildside-cli/src/tests/feature_flag_steps.rs (3)

1-11: LGTM!

Module-level feature gate #![cfg(not(feature = "store-sqlite"))] correctly ensures these tests only compile when store-sqlite is disabled, which is necessary because rstest-bdd scenarios are compile-time resolved.


13-52: Test world implementation is sound.

RefCell usage for interior mutability in the test world is appropriate for BDD-style tests where steps mutate shared state. The .expect() calls are acceptable within test code.


82-97: Assertion logic is correct.

The then step properly validates that MissingFeature is returned with the expected feature and action values. Using pattern matching with assertions inside is clear and idiomatic.

wildside-core/src/test_support.rs (4)

7-24: LGTM!

Feature-gated imports are correctly scoped. The combined #[cfg(all(any(test, feature = "test-support"), feature = "store-sqlite"))] gates ensure sqlite-related utilities compile only when both conditions are met.


26-65: LGTM!

MemoryStore remains unconditionally available for tests, providing a simple in-memory PoiStore implementation. The linear-scan approach is appropriate for small test datasets.


67-101: LGTM!

The sqlite test utilities are now correctly gated behind both test-support and store-sqlite features, ensuring they compile only when the underlying sqlite store functionality is available.


103-145: LGTM!

UnitTravelTimeProvider and TagScorer remain gated only by test-support, which is correct as they have no sqlite dependencies.

wildside-cli/src/lib.rs (5)

4-22: LGTM!

Feature-gated imports are correctly aligned with their usage in conditionally compiled functions. The unconditional OsmIngestSummary import supports the always-compiled IngestOutcome struct.


74-107: LGTM!

The execute_ingest implementation under store-sqlite correctly orchestrates the ingestion pipeline: OSM PBF parsing, POI persistence, Wikidata claim extraction, and spatial index writing. Error handling provides appropriate context for each failure point.


109-115: LGTM!

The fallback execute_ingest gracefully fails with a descriptive MissingFeature error when store-sqlite is disabled, guiding users to enable the required feature.


117-148: LGTM!

Helper functions are correctly feature-gated. The bz2 detection by extension and conditional decompression wrapping is appropriate for the use case.


200-258: LGTM!

IngestConfig and IngestOutcome are correctly unconditional, allowing CLI parsing to succeed before feature-gated execution paths are invoked.

wildside-cli/src/solve.rs (5)

8-24: LGTM!

Import gating is correctly structured. The OrtoolsSolver import appropriately excludes the VRP feature to prevent conflicts, and HttpTravelTimeProviderConfig remains unconditional for use in configuration parsing.


161-178: LGTM!

The store-sqlite implementation correctly wires up the store, scorer, and travel time provider, then delegates solver construction to build_solver_with_features for feature-based selection.


180-188: LGTM!

The fallback implementation returns a descriptive MissingFeature error when store-sqlite is disabled.


190-226: LGTM!

The build_solver_with_features function correctly implements solver priority: VRP when available, OR-Tools as fallback, and a clear error when neither is enabled. The feature gate combinations ensure only one implementation compiles per feature set.


300-330: Tests verify compile-time feature selection.

These tests serve as compile-time assertions that the feature gate logic is correctly configured. Whilst somewhat tautological (defining a constant then asserting its value), they document the expected solver priority and ensure each feature combination compiles.

wildside-core/src/store/sqlite.rs (9)

23-56: LGTM!

The SqlitePoiStoreError enum provides comprehensive error variants with clear messages and proper source attribution using thiserror.


58-69: LGTM!

SqlitePoiStore correctly holds only the in-memory R-tree, and the Debug implementation provides useful diagnostics without exposing internals.


71-96: LGTM!

The open method correctly opens the database read-only, loads the spatial index, and validates that all indexed POIs exist in the database. The R-tree is bulk-loaded for efficient spatial queries.


98-115: LGTM!

The PoiStore implementation correctly queries the R-tree and returns results sorted by ID for deterministic ordering.


117-131: LGTM!

The find_missing_poi_in_chunk function correctly identifies missing POIs using binary search. The caller deduplicates IDs beforehand, ensuring correct behaviour.


133-154: LGTM!

The validation logic correctly handles ID deduplication and chunked queries to stay within SQLite's parameter limits.


156-159: LGTM!

Using the conservative 999-parameter limit ensures compatibility across SQLite versions. The signature allows future enhancement to query runtime limits.


161-190: LGTM!

The chunked POI loading correctly builds parameterised queries, parses JSON tags with proper error context, and returns sorted results for efficient lookup.


192-367: LGTM!

Comprehensive test coverage including happy paths and error scenarios. The use of rstest fixtures provides clean test setup, and error cases validate all SqlitePoiStoreError variants.

Comment thread docs/execplans/phase-4-feature-flags.md Outdated
Comment thread docs/execplans/phase-4-feature-flags.md Outdated
Comment thread docs/users-guide.md
Comment thread wildside-cli/Cargo.toml
Comment thread wildside-cli/src/tests/feature_flag_steps.rs
Comment thread wildside-cli/src/tests/feature_flags.rs Outdated
Comment thread wildside-cli/src/tests/feature_flags.rs Outdated
Comment thread wildside-core/Cargo.toml Outdated
Comment thread wildside-core/src/store/spatial_index.rs
Comment thread wildside-solver-ortools/src/lib.rs
…ctions

The monolithic `wildside-engine-design.md` document has been restructured into multiple smaller markdown files, each representing a distinct section of the design. This improves maintainability and readability by keeping files under 400 lines, and allows sequential reading from introduction through the architecture, scoring, solver, implementation strategy, and works cited. The new files include:

- 01-introduction.md
- 02-data-foundation.md
- 03-scoring-personalization.md
- 04-library-architecture.md
- 05-solver-implementation.md
- 06-implementation-testing-deployment.md
- 99-works-cited.md

This also establishes a clear document structure and facilitates easier updates and navigation for contributors and reviewers.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Implement Phase 4 feature flags with tests and deployment Phase 4: Introduce root engine facade and feature flags Jan 2, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 29

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f37fcc6 and 544c106.

📒 Files selected for processing (8)
  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/03-scoring-personalization.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/99-works-cited.md
🧰 Additional context used
📓 Path-based instructions (4)
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
When new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve, proactively update the relevant file(s) in the docs/ directory to reflect the latest state. Ensure the documentation remains accurate and current.
Documentation must use en-GB-oxendict spelling and grammar ("-ize" / "-yse" / "-our") with the exception of the LICENSE filename.

docs/**/*.md: Use British English based on the Oxford English Dictionary locale en-GB, including -ize suffixes (e.g., realize, organization), -lyse suffixes (e.g., analyse, paralyse), -our suffixes (e.g., colour, behaviour), -re suffixes (e.g., centre, calibre), double 'l' (e.g., cancelled, counsellor), maintained 'e' (e.g., likeable, rateable), -ogue suffixes (e.g., analogue, catalogue)
Use the Oxford comma in documentation: 'ships, planes, and hovercraft' where it aids comprehension
Treat company names as collective nouns, for example: 'Concordat Industries are expanding'
Write headings in sentence case
Use Markdown headings (#, ##, ###, and so on) in order without skipping levels
Follow markdownlint recommendations
Provide code blocks and lists using standard Markdown syntax
Always provide a language identifier for fenced code blocks; use 'plaintext' for non-code text
Use '-' as the first level bullet and renumber lists when items change
Prefer inline links using 'text' or angle brackets around the URL
Ensure blank lines before and after bulleted lists and fenced blocks
Ensure tables have a delimiter line below the header row
Expand any uncommon acronym on first use, for example, Continuous Integration (CI)
Wrap paragraphs at 80 columns
Do not wrap tables
Use GitHub-flavoured numeric footnotes referenced as '[^1]'
Footnotes must be numbered in order of appearance in the document
Caption ev...

Files:

  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/03-scoring-personalization.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/99-works-cited.md
  • docs/wildside-engine-design.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Markdown tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/03-scoring-personalization.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/99-works-cited.md
  • docs/wildside-engine-design.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/03-scoring-personalization.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/99-works-cited.md
  • docs/wildside-engine-design.md
docs/**/!(README).md

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Avoid first and second person personal pronouns outside the README.md file

Files:

  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/03-scoring-personalization.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/99-works-cited.md
  • docs/wildside-engine-design.md
docs/**/*.{md,rs}

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Wrap code at 120 columns

Files:

  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/03-scoring-personalization.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/99-works-cited.md
  • docs/wildside-engine-design.md
🪛 LanguageTool
docs/wildside-engine-design/04-library-architecture.md

[uncategorized] ~26-~26: Loose punctuation mark.
Context: ...rates when necessary. - wildside-core: This crate is the heart of the engine. ...

(UNLIKELY_OPENING_PUNCTUATION)


[misspelling] ~38-~38: Did you mean “side effect” (=adverse effect, unintended consequence)? Open compounds are not hyphenated.
Context: ...`. - This crate is deterministic and side-effect free, making it easy to test rigoro...

(AFFECT_EFFECT)


[uncategorized] ~41-~41: Loose punctuation mark.
Context: ... testing and fuzzing. - wildside-data: Contains the ETL logic and data adapter...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~62-~62: Loose punctuation mark.
Context: ...ge the C++ dependency. - wildside-cli: A small command-line application for op...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~64-~64: Loose punctuation mark.
Context: ...ion for operational tasks. - ingest: Runs the full ETL pipeline from `wildsi...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~79-~79: Loose punctuation mark.
Context: ...obal popularity scores. - solve: Runs the route solver from the command ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~148-~148: Loose punctuation mark.
Context: ...zed, read-only artefacts: - pois.db: An SQLite (or RocksDB) file containing ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~152-~152: Loose punctuation mark.
Context: ...n a major release. - pois.rstar: A serialized R*-tree file for fast spa...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~156-~156: Loose punctuation mark.
Context: ...ross 0.x releases. - popularity.bin: A compact binary file of pre-calculated...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~164-~164: Possible missing comma found.
Context: ...rry a wikidata tag, the ETL is skipped but the claims schema is still initialized ...

(AI_HYDRA_LEO_MISSING_COMMA)


[misspelling] ~182-~182: Did you mean “side effect” (=adverse effect, unintended consequence)? Open compounds are not hyphenated.
Context: ...ign choice means the engine itself is side-effect free during a request. It allows applic...

(AFFECT_EFFECT)

docs/wildside-engine-design/05-solver-implementation.md

[typographical] ~1-~1: To join two clauses or introduce examples, consider using an em dash.
Context: # Section 4: The Solver Implementation - Abstracted and Replaceable This section...

(DASH_RULE)


[style] ~18-~18: Would you like to use the Oxford spelling “synchronization”? The spelling ‘synchronisation’ is also correct.
Context: ...void shared mutable state or use proper synchronisation to maintain thread safety. This abstrac...

(OXFORD_SPELLING_Z_NOT_S)


[grammar] ~36-~36: Please add a punctuation mark at the end of paragraph.
Context: ...ame.15 This implementation will live in the wildside-solver-vrp crate. ### 4.2....

(PUNCTUATION_PARAGRAPH_END)


[style] ~86-~86: The term ‘absolutely need’ is used quite frequently. Consider using a less common alternative to set your writing apart from others.
Context: ...o opt into this complexity only if they absolutely need it, without burdening the default setup...

(ABSOLUTELY_GREAT_RULE)


[misspelling] ~119-~119: This word is normally spelled as one.
Context: ...st. When called from within an existing multi-threaded Tokio runtime (detected via `Handle::...

(EN_COMPOUNDS_MULTI_THREADED)

docs/wildside-engine-design/06-implementation-testing-deployment.md

[locale-violation] ~11-~11: license must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...e licensed under the permissive ISC license, satisfying the project's legal requi...

(LICENCE_LICENSE_NOUN_SINGULAR)


[uncategorized] ~19-~19: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...e Flags:** The engine uses feature flags so consumers can select the solver and s...

(COMMA_COMPOUND_SENTENCE_2)


[uncategorized] ~23-~23: Loose punctuation mark.
Context: ...["solver-vrp", "store-sqlite", "serde"]`: The default build includes the nati...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~27-~27: Loose punctuation mark.
Context: ...equest/response types. - solver-vrp: Enables the native Rust solver backed b...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~30-~30: Loose punctuation mark.
Context: ...tures are enabled. - solver-ortools: Enables the optional OR-Tools solver ba...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~33-~33: Loose punctuation mark.
Context: ...T integration lands. - store-sqlite: Enables the SQLite-backed POI store and...

(UNLIKELY_OPENING_PUNCTUATION)


[style] ~69-~69: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ed as a JSON file containing a complete problem instance and its expected solution. The...

(EN_REPEATEDWORDS_PROBLEM)


[uncategorized] ~72-~72: Loose punctuation mark.
Context: ...solution. The schema captures: - pois: Array of POIs with id, coordinates, and...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~73-~73: Loose punctuation mark.
Context: ...and tags. - travel_time_matrix_seconds: Integer matrix of travel times (avoids ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~75-~75: Loose punctuation mark.
Context: ...s float precision issues). - request: A complete SolveRequest specification...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~77-~77: Loose punctuation mark.
Context: ...ed, and optional max_nodes. - expected: The expected route POI IDs in order, sc...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~82-~82: Loose punctuation mark.
Context: ...udes: - FixedMatrixTravelTimeProvider: A TravelTimeProvider implementation t...

(UNLIKELY_OPENING_PUNCTUATION)


[style] ~86-~86: Would you like to use the Oxford spelling “parameterized”? The spelling ‘parameterised’ is also correct.
Context: ...xternal routing dependencies. - rstest parameterised unit tests (golden_routes.rs): A sing...

(OXFORD_SPELLING_Z_NOT_S)


[style] ~87-~87: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ates over all JSON fixtures, loads each problem instance, constructs the solver with ...

(EN_REPEATEDWORDS_PROBLEM)


[style] ~144-~144: Since ownership is already implied, this phrasing may be redundant.
Context: ...main application, it can be promoted to its own repository with no code churn, as the b...

(PRP_OWN)

docs/wildside-engine-design/01-introduction.md

[style] ~16-~16: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ithms, and solving NP-hard optimization problems all align with the language's strengths...

(EN_REPEATEDWORDS_PROBLEM)


[uncategorized] ~65-~65: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...`, and tag map) directly in the R*-tree so lookups can avoid secondary hash-map pr...

(COMMA_COMPOUND_SENTENCE_2)


[uncategorized] ~66-~66: Possible missing comma found.
Context: ...secondary hash-map probes. During start-up the store reads these entries, valida...

(AI_HYDRA_LEO_MISSING_COMMA)


[style] ~85-~85: Would you like to use the Oxford spelling “normalized”? The spelling ‘normalised’ is also correct.
Context: ...non-negative values. Scores SHOULD be normalised to the [0.0, 1.0] range unless explic...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~148-~148: Possible missing comma found.
Context: ...operate exclusively on these types. At runtime the SqlitePoiStore provides the fast-...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~152-~152: Possible missing comma found.
Context: ...malformed tag payloads. Once validation succeeds the database connection is released, an...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~174-~174: Possible missing comma found.
Context: ... the application queries the SQLite POI store which uses an R-tree to locate points w...

(AI_HYDRA_LEO_MISSING_COMMA)

docs/wildside-engine-design/03-scoring-personalization.md

[typographical] ~86-~86: Two consecutive dots
Context: ...ching themes and clamps the result to 0.0..=1.0. Combining popularity and relevanc...

(DOUBLE_PUNCTUATION)

docs/wildside-engine-design/02-data-foundation.md

[typographical] ~1-~1: To join two clauses or introduce examples, consider using an em dash.
Context: # Section 1: The Data Foundation - Ingesting and Integrating Open Data The...

(DASH_RULE)


[grammar] ~30-~30: Please add a punctuation mark at the end of paragraph.
Context: ...quence of independent "blobs," a design that osmpbf leverages to process these bl...

(PUNCTUATION_PARAGRAPH_END)


[locale-violation] ~38-~38: licenses must be spelled with a “c” when used as a noun in British English. Use “licences”.
Context: ...l-licensed under the Apache-2.0 and MIT licenses, which are standard, permissive license...

(LICENCE_LICENSE_NOUN_PLURAL)


[locale-violation] ~39-~39: licenses must be spelled with a “c” when used as a noun in British English. Use “licences”.
Context: ...icenses, which are standard, permissive licenses suitable for commercial software develo...

(LICENCE_LICENSE_NOUN_PLURAL)


[style] ~45-~45: This expression can be considered offensive.
Context: ...rate is licensed under the "Do What The Fuck You Want To Public License, Version 2" (WTF...

(PROFANITY)


[locale-violation] ~46-~46: License must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...he "Do What The Fuck You Want To Public License, Version 2" (WTFPLv2).3 While extremely...

(LICENCE_LICENSE_NOUN_SINGULAR)


[locale-violation] ~58-~58: License must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...Parser Crates | Crate Name | Primary License | Key Technical Features ...

(LICENCE_LICENSE_NOUN_SINGULAR)


[locale-violation] ~61-~61: license must be spelled with a “c” when used as a noun in British English. Use “licence”.
Context: ...hs ago. | Not Recommended. The WTFPLv2 license introduces unnecessary legal risk for a...

(LICENCE_LICENSE_NOUN_SINGULAR)


[style] ~72-~72: The serial comma (Oxford comma, Harvard comma) is missing.
Context: ...Reader::par_map_reduceto count nodes, ways and relations while computing ageo::Rect`...

(SERIAL_COMMA_ON)


[uncategorized] ~88-~88: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...resolved after both passes emit warnings so operators can investigate fixture gaps ...

(COMMA_COMPOUND_SENTENCE_2)


[grammar] ~102-~102: Please add a punctuation mark at the end of paragraph.
Context: ...built tool for this task.5 For storage, the wd2sql tool provides an excellent te...

(PUNCTUATION_PARAGRAPH_END)


[uncategorized] ~125-~125: The preposition ‘to’ seems more likely in this position.
Context: ...ous reqwest client, issues requests on the Tokio runtime, and always sends a d...

(AI_HYDRA_LEO_REPLACE_ON_TO)


[style] ~163-~163: You have used the passive voice repeatedly in nearby sentences. To make your writing clearer and easier to read, consider using active voice.
Context: ... referenced by the PoiEntityLinks set are processed further. For those entities, the parser...

(REP_PASSIVE_VOICE)


[uncategorized] ~219-~219: Do not mix variants of the same word (‘initialisation’ and ‘initialization’) within a single text.
Context: ...e_data::wikidata::storemodule. Schema initialisation is handled by theinitialise_schema` f...

(EN_WORD_COHERENCY)


[style] ~220-~220: Would you like to use the Oxford spelling “initialize”? The spelling ‘initialise’ is also correct.
Context: ...Schema initialisation is handled by the initialise_schema function, which enables foreign...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~221-~221: Do not mix variants of the same word (‘normalise’ and ‘normalize’) within a single text.
Context: ...reign keys and creates a compact set of normalised tables: - wikidata_entities contains...

(EN_WORD_COHERENCY)


[uncategorized] ~232-~232: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ... poi_wikidata_claims joins both tables so the scoring pipeline can resolve a POI'...

(COMMA_COMPOUND_SENTENCE_2)


[uncategorized] ~244-~244: Possible missing preposition found.
Context: ... thousands of queries per user request. Will be rate-limited and result in extreme l...

(AI_HYDRA_LEO_MISSING_IT)


[grammar] ~273-~273: Please add a punctuation mark at the end of paragraph.
Context: ... to work seamlessly with the types from the geo crate. The practical implementat...

(PUNCTUATION_PARAGRAPH_END)

docs/wildside-engine-design/99-works-cited.md

[typographical] ~3-~3: To join two clauses or introduce examples, consider using an em dash.
Context: ...ks cited 1. Rust (programming language) - Wikipedia, accessed on August 13, 2025, ...

(DASH_RULE)


[inconsistency] ~3-~3: A new year has begun. Did you mean “August 13, 2026”?
Context: ...ming language) - Wikipedia, accessed on August 13, 2025, <https://en.wikipedia.org/wiki/Rust...

(DATE_NEW_YEAR)


[typographical] ~6-~6: To join two clauses or introduce examples, consider using an em dash.
Context: ...Rust_(programming_language)> 2. osmpbf - crates.io: Rust Pack...

(DASH_RULE)


[inconsistency] ~7-~7: A new year has begun. Did you mean “August 13, 2026”?
Context: ...: Rust Package Registry, accessed on August 13, 2025, https://crates.io/crates/osmpbf 3. ...

(DATE_NEW_YEAR)


[typographical] ~9-~9: To join two clauses or introduce examples, consider using an em dash.
Context: ...rates.io/crates/osmpbf> 3. osmpbfreader - crates.io: Rust Pack...

(DASH_RULE)


[inconsistency] ~10-~10: A new year has begun. Did you mean “August 13, 2026”?
Context: ...: Rust Package Registry, accessed on August 13, 2025, <https://crates.io/crates/osmpbfreader...

(DATE_NEW_YEAR)


[typographical] ~12-~12: To join two clauses or introduce examples, consider using an em dash.
Context: ...//crates.io/crates/osmpbfreader> 4. osm - Keywords - crates.io...

(DASH_RULE)


[typographical] ~12-~12: To join two clauses or introduce examples, consider using an em dash.
Context: .../crates/osmpbfreader> 4. osm - Keywords - crates.io: Rust Pack...

(DASH_RULE)


[inconsistency] ~13-~13: A new year has begun. Did you mean “August 13, 2026”?
Context: ...: Rust Package Registry, accessed on August 13, 2025, https://crates.io/keywords/osm 5. r...

(DATE_NEW_YEAR)


[typographical] ~15-~15: To join two clauses or introduce examples, consider using an em dash.
Context: ...at/vrp: A Vehicle Routing Problem solver - GitHub, accessed on August 13, 2025, ...

(DASH_RULE)


[inconsistency] ~16-~16: A new year has begun. Did you mean “August 13, 2026”?
Context: ...Problem solver - GitHub, accessed on August 13, 2025, <https://github.com/reinterpretcat/vrp...

(DATE_NEW_YEAR)


[typographical] ~18-~18: To join two clauses or introduce examples, consider using an em dash.
Context: ...retcat/vrp> 6. CP-SAT Solver | OR-Tools - Google for Developers, accessed on Augus...

(DASH_RULE)


[inconsistency] ~18-~18: A new year has begun. Did you mean “August 13, 2026”?
Context: ...ls - Google for Developers, accessed on August 13, 2025, <https://developers.google.com/optimiz...

(DATE_NEW_YEAR)


[typographical] ~21-~21: To join two clauses or introduce examples, consider using an em dash.
Context: ...om/optimization/cp/cp_solver> 7. cp_sat - Rust - Docs.rs, access...

(DASH_RULE)


[typographical] ~21-~21: To join two clauses or introduce examples, consider using an em dash.
Context: ...mization/cp/cp_solver> 7. cp_sat - Rust - Docs.rs, accessed on A...

(DASH_RULE)


[inconsistency] ~21-~21: A new year has begun. Did you mean “August 13, 2026”?
Context: ... Docs.rs, accessed on August 13, 2025, https://docs.rs/cp_sat 8. How to...

(DATE_NEW_YEAR)


[typographical] ~24-~24: Consider using an em dash in dialogues and enumerations.
Context: ... to use rust system binding API crates? - Reddit, accessed on August 13, 2025,...

(DASH_RULE)


[inconsistency] ~25-~25: A new year has begun. Did you mean “August 13, 2026”?
Context: ...ng API crates? - Reddit, accessed on August 13, 2025, <https://www.reddit.com/r/rust/comm...

(DATE_NEW_YEAR)


[typographical] ~28-~28: To join two clauses or introduce examples, consider using an em dash.
Context: ...system_binding_api_crates/> 9. wikidata - Rust - Docs.rs, access...

(DASH_RULE)


[typographical] ~28-~28: To join two clauses or introduce examples, consider using an em dash.
Context: ...binding_api_crates/> 9. wikidata - Rust - Docs.rs, accessed on A...

(DASH_RULE)


[inconsistency] ~28-~28: A new year has begun. Did you mean “August 13, 2026”?
Context: ... Docs.rs, accessed on August 13, 2025, https://docs.rs/wikidata 10. Geo...

(DATE_NEW_YEAR)


[typographical] ~31-~31: To join two clauses or introduce examples, consider using an em dash.
Context: ...ttps://docs.rs/wikidata> 10. Geospatial - Categories - [crates.io](http://crates.i...

(DASH_RULE)


[typographical] ~31-~31: To join two clauses or introduce examples, consider using an em dash.
Context: ...s/wikidata> 10. Geospatial - Categories - crates.io: Rust Pack...

(DASH_RULE)


[inconsistency] ~32-~32: A new year has begun. Did you mean “August 13, 2026”?
Context: ... Rust Package Registry, accessed on August 13, 2025, <https://crates.io/categories/scie...

(DATE_NEW_YEAR)


[typographical] ~35-~35: To join two clauses or introduce examples, consider using an em dash.
Context: ...o/categories/science::geo> 11. vrp_core - Rust - Docs.rs, access...

(DASH_RULE)


[typographical] ~35-~35: To join two clauses or introduce examples, consider using an em dash.
Context: ...ories/science::geo> 11. vrp_core - Rust - Docs.rs, accessed on A...

(DASH_RULE)


[inconsistency] ~35-~35: A new year has begun. Did you mean “August 13, 2026”?
Context: ... Docs.rs, accessed on August 13, 2025, <https://docs.rs/vrp-core/latest/v...

(DATE_NEW_YEAR)


[inconsistency] ~39-~39: A new year has begun. Did you mean “August 13, 2026”?
Context: ... Problems with Profits, accessed on August 13, 2025, <https://www.researchgate.net/publ...

(DATE_NEW_YEAR)


[typographical] ~42-~42: To join two clauses or introduce examples, consider using an em dash.
Context: ...ing_Problems_with_Profits> 13. vrp_core - Rust - Docs.rs, access...

(DASH_RULE)


[typographical] ~42-~42: To join two clauses or introduce examples, consider using an em dash.
Context: ...blems_with_Profits> 13. vrp_core - Rust - Docs.rs, accessed on A...

(DASH_RULE)


[inconsistency] ~42-~42: A new year has begun. Did you mean “August 13, 2026”?
Context: ... Docs.rs, accessed on August 13, 2025, https://docs.rs/vrp-core 14. VR...

(DATE_NEW_YEAR)


[typographical] ~45-~45: To join two clauses or introduce examples, consider using an em dash.
Context: ... https://docs.rs/vrp-core 14. VRP - Vehicle Routing Problem; ChatGPT-augment...

(DASH_RULE)


[typographical] ~45-~45: To join two clauses or introduce examples, consider using an em dash.
Context: ...hicle Routing Problem; ChatGPT-augmented - Stock/Inventory - Frappe Forum, acce...

(DASH_RULE)


[typographical] ~45-~45: To join two clauses or introduce examples, consider using an em dash.
Context: ...lem; ChatGPT-augmented - Stock/Inventory - Frappe Forum, accessed on August 13,...

(DASH_RULE)


[inconsistency] ~46-~46: A new year has begun. Did you mean “August 13, 2026”?
Context: ...ventory - Frappe Forum, accessed on August 13, 2025, <https://discuss.frappe.io/t/vrp-v...

(DATE_NEW_YEAR)


[typographical] ~49-~49: To join two clauses or introduce examples, consider using an em dash.
Context: ... Solving vehicle routing problem in Java - SoftwareMill, accessed on August 13,...

(DASH_RULE)


[inconsistency] ~49-~49: A new year has begun. Did you mean “August 13, 2026”?
Context: ...lem in Java - SoftwareMill, accessed on August 13, 2025, <https://softwaremill.com/solving-...

(DATE_NEW_YEAR)


[inconsistency] ~53-~53: A new year has begun. Did you mean “August 13, 2026”?
Context: ...// Lib.rs, accessed on August 13, 2025, https://lib.rs/crates/cp_sat

(DATE_NEW_YEAR)

docs/wildside-engine-design.md

[typographical] ~9-~9: To join two clauses or introduce examples, consider using an em dash.
Context: ...on.md) - [Section 1: The Data Foundation - Ingesting and Integrating Open Data](w...

(DASH_RULE)


[typographical] ~15-~15: To join two clauses or introduce examples, consider using an em dash.
Context: ... - [Section 4: The Solver Implementation - Abstracted and Replaceable](wildside-e...

(DASH_RULE)

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (10)
docs/wildside-engine-design/03-scoring-personalization.md (1)

1-88: Documentation is well-structured and accurate.

The scoring and personalisation design is clearly articulated. The offline/online boundary and data-artefact strategy align with the library-first architecture and feature-gated components described elsewhere in the design documentation.

docs/wildside-engine-design/99-works-cited.md (1)

53-54: Clarify the final bibliography entry: is it a duplicate or entry 16?

The last entry ("CP-SAT — Rust math library // Lib.rs") is not numbered and may be a duplicate of entry 7 (cp_sat – Rust – Docs.rs). Confirm whether this should be:

  • Removed as a duplicate.
  • Numbered as entry 16 if it is a distinct source.
  • Merged with entry 7 if it references the same resource.
docs/wildside-engine-design/01-introduction.md (6)

156-172: Validate sequence diagram and clarify notation.

The first sequence diagram (lines 156–172) correctly illustrates the startup flow. Ensure the notation is validated with make nixie.


177-190: Validate second sequence diagram syntax.

The second sequence diagram (lines 177–190) correctly shows the query flow. Validate via make nixie.


1-29: Introduction section complies with documentation standards.

The introductory paragraphs are well-structured, employ en-GB-oxendict spelling correctly ("realised", "emphasise" would be consistent, though "emphasis" is neutral), avoid first/second person pronouns, and establish clear narrative direction. The section sets appropriate context for the Rust-based design and aligns with the library-first architecture described in the PR objectives.


30-93: Core domain model list is clear and comprehensive.

The enumeration of PointOfInterest, Theme, InterestProfile, Route, PoiStore, SqlitePoiStore, TravelTimeProvider, Scorer, and test utilities is well-organised. Inline links to source files and clear descriptions of responsibilities and constraints (e.g., Scorer determinism and finiteness guarantees) provide excellent guidance for implementers.


95-105: Spatial indexing section clearly explains design rationale.

The explanation of SpatialIndex, its wrapping of rstar::RTree, and the build_spatial_index helper is lucid and well-motivated. The narrative about avoiding coupling by exposing read-only helpers aligns with library-first principles.


50-76: Verify link targets are valid and accessible.

Lines 56, 64, and subsequent documentation references link to crate internals (e.g., ../../wildside-core/src/store.rs). Ensure these relative paths resolve correctly from the published documentation context and that all referenced modules and types exist.

Verification approach

Confirm that:

  1. The file wildside-core/src/store.rs exports PoiStore trait.
  2. The file wildside-core/src/poi.rs exports PointOfInterest.
  3. Documentation builds without broken link warnings via cargo doc.
docs/wildside-engine-design.md (2)

1-2: Document structure is sound and navigation is clear.

The restructuring from monolithic design to a focussed, section-based index aligns with documentation best practices. The heading hierarchy (level 1 for title, level 2 for sections) is correct. The bullet list uses dashes as per guidelines.


8-18: All linked section files exist and are complete.

The index correctly references and links to all seven section files, each present with substantial content (54–282 lines). All files remain well under the 400-line guideline mentioned in the index. Markdown formatting, headings, and list structure conform to project standards. No issues detected.

Comment thread docs/wildside-engine-design.md Outdated
Comment thread docs/wildside-engine-design/01-introduction.md
Comment thread docs/wildside-engine-design/01-introduction.md Outdated
Comment thread docs/wildside-engine-design/01-introduction.md
Comment thread docs/wildside-engine-design/01-introduction.md Outdated
Comment thread docs/wildside-engine-design/06-implementation-testing-deployment.md Outdated
Comment thread docs/wildside-engine-design/06-implementation-testing-deployment.md Outdated
Comment thread docs/wildside-engine-design/06-implementation-testing-deployment.md
Comment thread docs/wildside-engine-design/06-implementation-testing-deployment.md Outdated
Comment thread docs/wildside-engine-design/99-works-cited.md
- Implement matrix testing for various feature flag combinations in CI
- Support testing with default, no-default, all-features, and specific features
- Improve CI to ensure compatibility across solver and store feature permutations

Additional changes:
- Adapt Makefile to accept TEST_FLAGS for feature gating during test runs
- Update solve.rs to select solver implementation based on enabled features
- Add SolveError::NotImplemented variant for placeholder solver implementations
- Refactor spatial index persistence format handling to explicit header+payload
- Enhance documentation for feature flags and project licensing terminology
- Guard CLI ingest and solve commands behind feature flags
- Remove serde from store-sqlite feature to better express dependencies

Overall, this enables rigorous testing and conditional compilation support for
feature-flagged components to improve code reliability and CI coverage.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Phase 4: Introduce root engine facade and feature flags Phase 4: Decompose oversized engine design docs Jan 3, 2026
- Wrap ingest-related code and tests with #[cfg(feature = "store-sqlite")] to enable optional compilation based on the SQLite store feature.
- Refactor imports and function definitions to conditionally include functionality only if the feature is enabled.
- Remove dummy execute_ingest fallback, consolidating ingest implementation behind feature flag.
- Update test modules and helpers with appropriate feature gating to isolate SQLite-dependent tests.

This modularizes the ingest CLI to support compilation without SQLite, improving build flexibility.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Phase 4: Decompose oversized engine design docs Phase 4: Implement root engine facade and feature flags Jan 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Fix all issues with AI Agents 🤖
In @.github/workflows/ci.yml:
- Line 71: Remove the explicit job dependency on build by deleting the "needs:
build" entry under the feature-matrix job so it can run in parallel with the
build job; only keep or reintroduce "needs: build" if the feature-matrix must be
gated on build's validation, otherwise ensure the feature-matrix job still
checks out code and uses its own rust-cache so it remains independent.
- Around line 75-77: Add an explicit test case for the intended default feature
combination by updating the CI matrix entry named "default-features": set its
test_flags to the explicit string "--no-default-features --features
solver-vrp,store-sqlite" so the workflow runs a job that disables default
features and enables the documented defaults (solver-vrp and store-sqlite),
ensuring the default configuration is verified and documented.

In @docs/wildside-engine-design/01-introduction.md:
- Line 85: Replace the British‑spelled word "normalised" with the en-GB-oxendict
preferred form "normalized" in the sentence fragment containing "Scores SHOULD
be normalised to the `[0.0, 1.0]` range..." so it reads "Scores SHOULD be
normalized to the `[0.0, 1.0]` range unless explicitly..."; update that single
token in the document (file: 01-introduction.md) to maintain the project's -ize
spelling convention.

In @docs/wildside-engine-design/02-data-foundation.md:
- Line 153: Replace the British-variant verb "normalises" with the
en-GB-oxendict-compliant spelling "normalizes" in the phrase that reads
"structure scans the ingested POIs, normalises `wikidata=*` tag values
(handling" so the line becomes "structure scans the ingested POIs, normalizes
`wikidata=*` tag values (handling"; update any other occurrences of "normalises"
in docs/wildside-engine-design/02-data-foundation.md accordingly to maintain
consistent -ize verb spelling.
- Around line 218-219: Change the British-English words in the documentation
sentence so they use the project's en-GB-oxendict -ize spelling: replace
"initialisation" with "initialization" and "normalised" with "normalized" in the
sentence that references the initialise_schema function (do not change the code
identifier `initialise_schema`).

In @docs/wildside-engine-design/05-solver-implementation.md:
- Around line 36-38: The sentence break before the crate reference is awkward
and missing terminal punctuation; edit the paragraph so the crate name
`wildside-solver-vrp` stays on the same line as the sentence and add the
appropriate punctuation (e.g., a period) at the end. Locate the sentence
mentioning the implementation and the crate name and merge the stray newline so
it reads as a single, complete sentence referencing `wildside-solver-vrp`.

In @docs/wildside-engine-design/06-implementation-testing-deployment.md:
- Around line 144-145: The sentence "it can be promoted to its a separate
repository" is malformed; edit the paragraph containing that phrase (the line
starting "different release cadence from the main application, it can be
promoted to its a separate repository") and remove the extraneous "its" so it
reads "...it can be promoted to a separate repository..." ensuring spacing and
punctuation remain correct.
- Line 86: Replace the British spelling "parameterised" with the Oxford/en-GB
"parameterized" in the document text where it appears in the phrase "rstest
parameterised unit tests (`golden_routes.rs`)" so the heading reads "rstest
parameterized unit tests (`golden_routes.rs`)" to conform to the requested -ize
convention.

In @Makefile:
- Line 20: The Makefile passes --features test-support globally but the root
package wildside-engine lacks that feature causing cargo to fail; either add a
test-support feature to the root Cargo.toml that simply re-exports/depends-on
the feature in wildside-core and wildside-solver-vrp, or change the Makefile
invocation so the feature is only enabled for the crates that define it (e.g.,
remove --features test-support from the global nextest call and instead run
nextest per-crate with --manifest-path pointing at wildside-core and
wildside-solver-vrp or use a workspace targeting mechanism that scopes features
to those crates).

In @wildside-core/src/store/sqlite.rs:
- Around line 78-79: The local bindings database_path and index_path are
unnecessarily allocating PathBufs with .as_ref().to_path_buf(); change them to
borrow the input paths instead (e.g., let database_path =
database_path.as_ref(); let index_path = index_path.as_ref();) and update any
subsequent uses to work with &Path rather than owned PathBuf (adjust function
signatures or clones where needed) so you avoid the extra allocations.
- Around line 117-131: The function find_missing_poi_in_chunk can hit a logic
path where the length check indicates a missing ID but the loop finds none;
replace the compile-time-only debug_assert! with a runtime panic by using
unreachable! to ensure this logic error is caught in release builds—update the
end of find_missing_poi_in_chunk to call unreachable!("chunk length mismatch
should reveal missing id") instead of debug_assert! and remove the trailing None
return so the function either returns Some(id) or panics.
- Around line 82-87: Remove the redundant clone of database_path when building
SqlitePoiStoreError::OpenDatabase: call Connection::open_with_flags with the
owned database_path (instead of &database_path) and in the map_err closure
construct the error using path: database_path.to_path_buf() (referencing
database_path, Connection::open_with_flags, and
SqlitePoiStoreError::OpenDatabase) so you don't clone the path for the error
case.

Caution

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

⚠️ Outside diff range comments (1)
wildside-cli/src/lib.rs (1)

265-272: IngestOutcome should be feature-gated.

IngestOutcome is only constructed within #[cfg(feature = "store-sqlite")] code. Gate the struct to prevent dead code warnings and clarify intent.

🔎 Proposed fix
+#[cfg(feature = "store-sqlite")]
 #[derive(Debug, Clone, PartialEq)]
 struct IngestOutcome {
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 544c106 and 76a91f7.

📒 Files selected for processing (22)
  • .github/workflows/ci.yml
  • Makefile
  • docs/execplans/phase-4-feature-flags.md
  • docs/users-guide.md
  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • wildside-cli/src/lib.rs
  • wildside-cli/src/solve.rs
  • wildside-cli/src/tests/feature_flags.rs
  • wildside-cli/src/tests/helpers.rs
  • wildside-cli/src/tests/steps.rs
  • wildside-cli/src/tests/unit.rs
  • wildside-core/Cargo.toml
  • wildside-core/src/solver.rs
  • wildside-core/src/store/spatial_index.rs
  • wildside-core/src/store/sqlite.rs
  • wildside-solver-ortools/src/lib.rs
  • wildside-solver-vrp/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (5)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make check-fmt, make lint, and make test before committing in Rust projects.
make check-fmt executes cargo fmt --workspace -- --check to validate formatting across the entire workspace without modifying files.
make lint executes cargo clippy --workspace --all-targets --all-features -- -D warnings to lint every target with all features enabled and deny all Clippy warnings.
make test executes cargo test --workspace to run the full workspace test suite.
Clippy warnings MUST be disallowed in Rust code.
Fix any warnings emitted during tests in the Rust code itself rather than silencing them.
Where a Rust function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a Rust function has too many parameters, group related parameters in meaningfully named structs.
Where a Rust function is returning a large error, consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality in Rust. Run both before and after making any change.
Every Rust module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings in Rust.
Handle errors with the Result type instead of panicking in Rust where feasible.
Avoid unsafe code in Rust unless absolutely necessary, and document any usage clearly.
Place function attributes after doc comments in Rust.
Do not use return in single-line Rust functions.
Use predicate functions for conditional criteria in Rust with more than two branches.
Rust lints must not be silenced except as a last resort.
Rust lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow in Rust.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated Rust tests with `#[rstest(...)...

Files:

  • wildside-core/src/solver.rs
  • wildside-cli/src/tests/unit.rs
  • wildside-cli/src/tests/feature_flags.rs
  • wildside-solver-ortools/src/lib.rs
  • wildside-solver-vrp/src/lib.rs
  • wildside-cli/src/solve.rs
  • wildside-core/src/store/spatial_index.rs
  • wildside-cli/src/tests/steps.rs
  • wildside-core/src/store/sqlite.rs
  • wildside-cli/src/lib.rs
  • wildside-cli/src/tests/helpers.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • wildside-core/src/solver.rs
  • wildside-cli/src/tests/unit.rs
  • wildside-cli/src/tests/feature_flags.rs
  • wildside-solver-ortools/src/lib.rs
  • wildside-solver-vrp/src/lib.rs
  • wildside-cli/src/solve.rs
  • wildside-core/src/store/spatial_index.rs
  • wildside-cli/src/tests/steps.rs
  • wildside-core/src/store/sqlite.rs
  • wildside-cli/src/lib.rs
  • wildside-cli/src/tests/helpers.rs
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
When new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve, proactively update the relevant file(s) in the docs/ directory to reflect the latest state. Ensure the documentation remains accurate and current.
Documentation must use en-GB-oxendict spelling and grammar ("-ize" / "-yse" / "-our") with the exception of the LICENSE filename.

docs/**/*.md: Use British English based on the Oxford English Dictionary locale en-GB, including -ize suffixes (e.g., realize, organization), -lyse suffixes (e.g., analyse, paralyse), -our suffixes (e.g., colour, behaviour), -re suffixes (e.g., centre, calibre), double 'l' (e.g., cancelled, counsellor), maintained 'e' (e.g., likeable, rateable), -ogue suffixes (e.g., analogue, catalogue)
Use the Oxford comma in documentation: 'ships, planes, and hovercraft' where it aids comprehension
Treat company names as collective nouns, for example: 'Concordat Industries are expanding'
Write headings in sentence case
Use Markdown headings (#, ##, ###, and so on) in order without skipping levels
Follow markdownlint recommendations
Provide code blocks and lists using standard Markdown syntax
Always provide a language identifier for fenced code blocks; use 'plaintext' for non-code text
Use '-' as the first level bullet and renumber lists when items change
Prefer inline links using 'text' or angle brackets around the URL
Ensure blank lines before and after bulleted lists and fenced blocks
Ensure tables have a delimiter line below the header row
Expand any uncommon acronym on first use, for example, Continuous Integration (CI)
Wrap paragraphs at 80 columns
Do not wrap tables
Use GitHub-flavoured numeric footnotes referenced as '[^1]'
Footnotes must be numbered in order of appearance in the document
Caption ev...

Files:

  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/execplans/phase-4-feature-flags.md
  • docs/users-guide.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/05-solver-implementation.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Markdown tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/execplans/phase-4-feature-flags.md
  • docs/users-guide.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/05-solver-implementation.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/execplans/phase-4-feature-flags.md
  • docs/users-guide.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/05-solver-implementation.md
docs/**/!(README).md

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Avoid first and second person personal pronouns outside the README.md file

Files:

  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/execplans/phase-4-feature-flags.md
  • docs/users-guide.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/05-solver-implementation.md
docs/**/*.{md,rs}

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Wrap code at 120 columns

Files:

  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/execplans/phase-4-feature-flags.md
  • docs/users-guide.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/05-solver-implementation.md
🧠 Learnings (2)
📚 Learning: 2026-01-03T14:22:19.441Z
Learnt from: leynos
Repo: leynos/wildside-engine PR: 81
File: docs/wildside-engine-design/02-data-foundation.md:0-0
Timestamp: 2026-01-03T14:22:19.441Z
Learning: Apply en-GB Oxford English spelling in Markdown files by preferring -ize (e.g., normalize, initialize, organize) over -ise, -yse over -yze (e.g., analyse, paralyse), and -our over -or (e.g., colour, favour). The Oxford British English convention favors -ize in these contexts. This guidance should be applied across Markdown documentation in the docs directory to maintain consistent spelling.

Applied to files:

  • docs/wildside-engine-design.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/execplans/phase-4-feature-flags.md
  • docs/users-guide.md
  • docs/wildside-engine-design/04-library-architecture.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/05-solver-implementation.md
📚 Learning: 2026-01-04T04:50:10.691Z
Learnt from: leynos
Repo: leynos/rstest-bdd PR: 384
File: crates/rstest-bdd/tests/async_step_infrastructure.rs:4-4
Timestamp: 2026-01-04T04:50:10.691Z
Learning: In en-GB-oxendict (Oxford English) spelling convention, verbs use the -ize ending (e.g., "normalizes," "organizes," "realizes"), not -ise. The convention is specifically -ize / -yse / -our, so "normalizes" is correct in rstest-bdd documentation and comments.

Applied to files:

  • docs/wildside-engine-design/02-data-foundation.md
🧬 Code graph analysis (6)
wildside-cli/src/tests/feature_flags.rs (1)
wildside-cli/src/lib.rs (1)
  • run_ingest (69-83)
wildside-solver-ortools/src/lib.rs (1)
wildside-core/src/solver.rs (1)
  • solve (179-179)
wildside-cli/src/solve.rs (2)
wildside-fs/src/lib.rs (1)
  • open_utf8_file (10-12)
wildside-solver-ortools/src/lib.rs (1)
  • new (32-38)
wildside-core/src/store/spatial_index.rs (1)
wildside-core/src/store/sqlite.rs (2)
  • open (73-95)
  • poi (204-210)
wildside-core/src/store/sqlite.rs (5)
wildside-core/src/store/spatial_index.rs (2)
  • load_index_entries (129-166)
  • poi (182-188)
wildside-core/src/store/mod.rs (1)
  • get_pois_in_bbox (71-74)
wildside-core/src/test_support.rs (2)
  • get_pois_in_bbox (52-64)
  • write_sqlite_database (69-92)
wildside-core/tests/sqlite_poi_store_behaviour.rs (2)
  • bbox (78-80)
  • new (29-38)
wildside-core/src/poi.rs (1)
  • envelope (46-48)
wildside-cli/src/lib.rs (8)
wildside-core/tests/poi_store_behaviour.rs (1)
  • store (19-22)
wildside-core/src/store/spatial_index.rs (1)
  • write_spatial_index (90-95)
wildside-cli/src/tests/helpers.rs (1)
  • wikidata (87-89)
wildside-data/src/wikidata/etl/mod.rs (1)
  • extract_linked_entity_claims (153-198)
wildside-data/src/wikidata/store/persistence.rs (1)
  • persist_claims_to_path (285-295)
wildside-data/src/ingest/mod.rs (1)
  • ingest_osm_pbf_report (149-202)
wildside-data/src/ingest/sqlite.rs (1)
  • persist_pois_to_sqlite (97-122)
wildside-cli/src/solve.rs (2)
  • into_config (105-108)
  • try_from (160-187)
🪛 LanguageTool
docs/wildside-engine-design.md

[typographical] ~10-~10: To join two clauses or introduce examples, consider using an em dash.
Context: ...on.md) - [Section 1: The Data Foundation - Ingesting and Integrating Open Data](w...

(DASH_RULE)


[typographical] ~16-~16: To join two clauses or introduce examples, consider using an em dash.
Context: ... - [Section 4: The Solver Implementation - Abstracted and Replaceable](wildside-e...

(DASH_RULE)

docs/wildside-engine-design/01-introduction.md

[style] ~16-~16: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ithms, and solving NP-hard optimization problems all align with the Rust ecosystem's str...

(EN_REPEATEDWORDS_PROBLEM)


[uncategorized] ~65-~65: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...`, and tag map) directly in the R*-tree so lookups can avoid secondary hash-map pr...

(COMMA_COMPOUND_SENTENCE_2)


[style] ~85-~85: Would you like to use the Oxford spelling “normalized”? The spelling ‘normalised’ is also correct.
Context: ...non-negative values. Scores SHOULD be normalised to the [0.0, 1.0] range unless explic...

(OXFORD_SPELLING_Z_NOT_S)

docs/execplans/phase-4-feature-flags.md

[uncategorized] ~13-~13: The hyphen in fully-tested is redundant.
Context: ...le keeping the default build stable and fully-tested. Success is observable when the workspa...

(ADVERB_LY_HYPHEN_FIX)


[style] ~87-~87: Consider using the typographical ellipsis character here instead.
Context: ...lite, with conditional compilation via #[cfg(feature = "...")]. Testing guidance is in docs/rust...

(ELLIPSIS)


[style] ~122-~122: Consider using the typographical ellipsis character here instead.
Context: ...y re-export the SQLite store and to add doc(cfg(...)) hints where appropriate. Adjust exis...

(ELLIPSIS)


[style] ~125-~125: Consider using a more formal replacement.
Context: ... compiled when the feature is enabled. After that, update wildside-cli to respect the f...

(AFTER_THAT)


[style] ~141-~141: Consider using the typographical ellipsis character here instead.
Context: ...and scenarios are guarded with the same #[cfg(feature = "...")] selectors so they only compile unde...

(ELLIPSIS)


[style] ~183-~183: Consider removing “of” to be more concise
Context: ...ceptance The change is acceptable when all of the following are true: - make check-fmt...

(ALL_OF_THE)


[uncategorized] ~185-~185: Loose punctuation mark.
Context: ... following are true: - make check-fmt, make lint, and make test succeed wi...

(UNLIKELY_OPENING_PUNCTUATION)


[style] ~198-~198: Consider using the typographical ellipsis character here instead.
Context: ... that the module is behind the intended #[cfg(feature = "...")] guard and that its dependencies are...

(ELLIPSIS)


[uncategorized] ~216-~216: Loose punctuation mark.
Context: ...nifest with these names: - solver-vrp: enables wildside-solver-vrp. - `solve...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~217-~217: Loose punctuation mark.
Context: ...wildside-solver-vrp. - solver-ortools: enables wildside-solver-ortools. - s...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~218-~218: Loose punctuation mark.
Context: ...ldside-solver-ortools. - store-sqlite`: enables the SQLite-backed POI store. T...

(UNLIKELY_OPENING_PUNCTUATION)

docs/wildside-engine-design/04-library-architecture.md

[uncategorized] ~26-~26: Loose punctuation mark.
Context: ...rates when necessary. - wildside-core: This crate is the heart of the engine. ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~41-~41: Loose punctuation mark.
Context: ... testing and fuzzing. - wildside-data: Contains the ETL logic and data adapter...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~62-~62: Loose punctuation mark.
Context: ...ge the C++ dependency. - wildside-cli: A small command-line application for op...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~64-~64: Loose punctuation mark.
Context: ...ion for operational tasks. - ingest: Runs the full ETL pipeline from `wildsi...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~79-~79: Loose punctuation mark.
Context: ...obal popularity scores. - solve: Runs the route solver from the command ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~148-~148: Loose punctuation mark.
Context: ...zed, read-only artefacts: - pois.db: An SQLite (or RocksDB) file containing ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~152-~152: Loose punctuation mark.
Context: ...n a major release. - pois.rstar: A serialized R*-tree file for fast spa...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~156-~156: Loose punctuation mark.
Context: ...ross 0.x releases. - popularity.bin: A compact binary file of pre-calculated...

(UNLIKELY_OPENING_PUNCTUATION)

docs/wildside-engine-design/02-data-foundation.md

[typographical] ~1-~1: To join two clauses or introduce examples, consider using an em dash.
Context: # Section 1: The Data Foundation - Ingesting and Integrating Open Data The...

(DASH_RULE)


[grammar] ~30-~30: Please add a punctuation mark at the end of paragraph.
Context: ...quence of independent "blobs," a design that osmpbf leverages to process these bl...

(PUNCTUATION_PARAGRAPH_END)


[style] ~45-~45: This expression can be considered offensive.
Context: ...rate is licensed under the "Do What The Fuck You Want To Public Licence, Version 2" (WTF...

(PROFANITY)


[uncategorized] ~123-~123: The preposition ‘to’ seems more likely in this position.
Context: ...ous reqwest client, issues requests for the Tokio runtime, and always sends a d...

(AI_HYDRA_LEO_REPLACE_FOR_TO)


[style] ~153-~153: Would you like to use the Oxford spelling “normalizes”? The spelling ‘normalises’ is also correct.
Context: ...nksstructure scans the ingested POIs, normaliseswikidata=*` tag values (handling commo...

(OXFORD_SPELLING_Z_NOT_S)


[style] ~161-~161: You have used the passive voice repeatedly in nearby sentences. To make your writing clearer and easier to read, consider using active voice.
Context: ... referenced by the PoiEntityLinks set are processed further. For those entities, the parser...

(REP_PASSIVE_VOICE)


[style] ~218-~218: Would you like to use the Oxford spelling “initialize”? The spelling ‘initialise’ is also correct.
Context: ...Schema initialization is handled by the initialise_schema function, which enables foreign...

(OXFORD_SPELLING_Z_NOT_S)


[style] ~219-~219: Would you like to use the Oxford spelling “normalized”? The spelling ‘normalised’ is also correct.
Context: ...reign keys and creates a compact set of normalised tables: - wikidata_entities contains...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~242-~242: Possible missing preposition found.
Context: ... thousands of queries per user request. Will be rate-limited and result in extreme l...

(AI_HYDRA_LEO_MISSING_IT)


[grammar] ~271-~271: Please add a punctuation mark at the end of paragraph.
Context: ... to work seamlessly with the types from the geo crate. The practical implementat...

(PUNCTUATION_PARAGRAPH_END)

docs/wildside-engine-design/06-implementation-testing-deployment.md

[style] ~69-~69: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ed as a JSON file containing a complete problem instance and its expected solution. The...

(EN_REPEATEDWORDS_PROBLEM)


[style] ~86-~86: Would you like to use the Oxford spelling “parameterized”? The spelling ‘parameterised’ is also correct.
Context: ...ernal routing dependencies. - rstest parameterised unit tests (golden_routes.rs): A si...

(OXFORD_SPELLING_Z_NOT_S)

docs/wildside-engine-design/05-solver-implementation.md

[grammar] ~36-~36: Please add a punctuation mark at the end of paragraph.
Context: ...ame.15 This implementation will live in the wildside-solver-vrp crate. ### 4.2....

(PUNCTUATION_PARAGRAPH_END)


[style] ~86-~86: The term ‘absolutely need’ is used quite frequently. Consider using a less common alternative to set your writing apart from others.
Context: ...o opt into this complexity only if they absolutely need it, without burdening the default setup...

(ABSOLUTELY_GREAT_RULE)

🔍 Remote MCP Context7

Let me search for information about the testing framework and feature flag patterns being used:Now let me search for information about GitHub Actions feature matrix testing and spatial indexing patterns:

Summary of Relevant Context for PR Review

Testing Framework (rstest-bdd)

rstest-bdd brings the collaborative clarity of BDD to Rust without asking you to adopt a bespoke runner or a monolithic "world" object, instead building on the excellent rstest fixture and parametrisation model. The PR upgrades this from 0.2.0 to 0.3.0. Key point: In version 0.3.0, the #[scenario] attribute now requires a path argument pointing to the .feature file; index-only usage is no longer supported. This means any BDD tests in the PR using rstest-bdd 0.3.0 must reference feature files with explicit paths (as seen in wildside-cli/src/tests/feature_flag_steps.rs).

Feature Flag Implementation Best Practices

The PR's approach to feature flags aligns with Rust/Cargo conventions. Dependencies automatically enable default features unless default-features = false is specified, which can make it difficult to ensure that the default features are not enabled, especially for a dependency that appears multiple times in the dependency graph. The PR correctly handles this by:

  • Declaring optional dependencies with optional = true
  • Creating explicit feature definitions (e.g., solver-vrp, solver-ortools, store-sqlite)
  • Using #[cfg(feature = "store-sqlite")] guards in code

GitHub Actions Matrix Strategy

The matrix strategy runs jobs across multiple configurations - different OS versions, language versions, or other parameters, testing your application across environments without duplicating workflows. The PR's new feature-matrix job in .github/workflows/ci.yml uses this pattern to test multiple feature combinations. Matrix strategies improve continuous integration practices by executing numerous job combinations simultaneously, and this parallel execution reduces runtime, allowing developers to receive test feedback more swiftly.

Spatial Indexing (R-tree/rstar)

rstar is a flexible, n-dimensional r*-tree implementation for the Rust ecosystem suitable for use as a spatial index, with primitives provided for point, line, and rectangle geometries. R-trees are suited best if many queries and only few insertions are made, and rstar supports bulk loading which cuts down the constant factors when creating an r-tree significantly compared to sequential insertions. The PR's approach to persist R-tree indices using bincode serialization and a header format (WSPI magic + version) aligns with the library's design for read-heavy, indexed spatial data access.

Key Validation Points for Review

  1. rstest-bdd 0.3.0 compatibility: Verify that all #[scenario] attributes include path = "..." parameters pointing to .feature files
  2. Feature flag consistency: Confirm that all public API re-exports in src/lib.rs and CLI modules are consistently gated behind matching feature flags across crates
  3. Cargo resolver behavior: With resolver = "2", --no-default-features disables defaults for all workspace members, not just the root
  4. Spatial index persistence: The custom binary format (magic + version + bincode payload) correctly supports versioning for future migrations and validation

[::web_search::], [::Deepwiki::],

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (48)
Makefile (1)

9-9: LGTM!

The TEST_FLAGS variable declaration follows Makefile conventions and integrates cleanly with the CI feature-matrix job.

.github/workflows/ci.yml (2)

93-106: LGTM!

The job steps correctly set up the Rust toolchain, caching, and nextest, then execute tests with the appropriate TEST_FLAGS. The matrix strategy with fail-fast: false ensures all feature combinations are tested independently.


69-92: The placeholder solver-ortools crate is properly designed to handle gracefully in matrix tests. The OrtoolsSolver::solve() method returns SolveError::NotImplemented rather than panicking, allowing matrix entries solver-ortools-only, solvers-only, and solver-ortools-and-store to compile and execute without test failures.

wildside-core/Cargo.toml (1)

23-26: Feature flag configuration is well-structured.

The dependency chain is logical:

  • serde provides serialization primitives for core data structures.
  • store-sqlite depends on serde and adds bincode for spatial index persistence and rusqlite for database access.
  • Defaults enable both, allowing consumers to opt out selectively via default-features = false.

This aligns with the PR's goal of conditionally exposing SQLite store behind feature gates.

wildside-cli/src/tests/steps.rs (2)

3-4: Feature gating is correctly applied.

The module-level #![cfg(feature = "store-sqlite")] gate ensures these BDD steps compile only when the SQLite store is available, aligning with the CLI's feature-gated ingestion logic.


185-199: Scenario registration syntax for rstest-bdd 0.3.0 is correct.

The macro correctly specifies path = "tests/features/ingest_command.feature" for all three scenarios, as required by rstest-bdd 0.3.0. The referenced .feature file exists at the expected path.

wildside-cli/src/tests/unit.rs (1)

3-4: Feature gating and test structure are sound.

The module-level #![cfg(feature = "store-sqlite")] correctly gates these unit tests, ensuring they compile only when SQLite store support is enabled. The tests properly validate IngestConfig error paths using rstest parameterization and appropriate assertions.

wildside-solver-vrp/src/lib.rs (1)

12-12: Documentation improvement enhances clarity.

The reference to SolveError::InvalidRequest is more precise than the previous generic "modelling errors" wording, making the error-handling contract explicit for consumers.

wildside-core/src/solver.rs (2)

163-165: NotImplemented variant is well-integrated.

This error variant provides a clear signal when a solver backend is not available, supporting the PR's feature-gated architecture. The implementation correctly derives Error via thiserror and is properly documented.


171-172: Trait documentation accurately reflects placeholder behaviour.

The updated documentation correctly guides implementers to return NotImplemented when a concrete backend is unavailable, which aligns with the feature-flagged solver backends introduced in this phase.

docs/wildside-engine-design.md (1)

3-20: LGTM! Clear structure and rationale.

The index file successfully delegates content to modular sections whilst providing a clear rationale for the 400-line guideline. The sequential reading flow is well-signposted.

wildside-cli/src/tests/feature_flags.rs (1)

1-31: LGTM! Efficient feature-flag test.

The test correctly verifies that run_ingest returns MissingFeature when store-sqlite is disabled. The module-level cfg gate is appropriate, and the test avoids unnecessary file I/O since the function returns early when the feature is absent.

docs/execplans/phase-4-feature-flags.md (1)

1-235: LGTM! Comprehensive ExecPlan documentation.

The document thoroughly captures Phase 4 feature-flag implementation with clear progress tracking, decision rationales, validation criteria, and concrete steps. The completed status and retrospective provide valuable context for future reference.

docs/wildside-engine-design/04-library-architecture.md (1)

1-185: LGTM! Comprehensive architecture documentation.

The library-first design is clearly articulated with well-defined crate boundaries, a stable API surface, and explicit offline/online separation. The artefact versioning strategy with migration guidance provides robust operational support.

docs/wildside-engine-design/05-solver-implementation.md (1)

1-150: Documentation structure and spelling are sound.

The solver implementation design document follows the decomposed structure well. Spelling adheres to en-GB-oxendict conventions (e.g., "synchronization", "modelling"). The markdownlint directive at line 99 is appropriate for the long trait signature.

docs/users-guide.md (3)

64-66: Documentation accurately reflects the new NotImplemented error variant.

The addition of SolveError::NotImplemented is correctly documented, aligning with the placeholder solver implementation in wildside-solver-ortools.


73-79: Spelling correction applied and feature-gating documented.

The "serialized" spelling now conforms to en-GB-oxendict. The store-sqlite feature-gating for SqlitePoiStore is accurately described.


107-108: Error handling summary correctly expanded.

The SolveError description now covers both InvalidRequest and NotImplemented variants, matching the updated API surface.

wildside-solver-ortools/src/lib.rs (1)

1-50: Well-structured placeholder solver crate.

The OrtoolsSolver placeholder reserves the API surface for future CP-SAT integration. The use of SolveError::NotImplemented correctly signals that the backend is not yet available, addressing the prior review feedback. The const fn constructor and forbid(unsafe_code) are appropriate choices.

wildside-cli/src/lib.rs (3)

69-83: Feature-gated ingest dispatch is well-structured.

The run_ingest function cleanly handles both feature states: returning MissingFeature when store-sqlite is disabled and delegating to execute_ingest when enabled. The let _ = args suppresses the unused variable warning appropriately.


92-125: Ingest pipeline implementation is correct.

The execute_ingest function properly sequences: OSM PBF ingestion → SQLite persistence → Wikidata claims extraction → claims persistence → spatial index writing. Error handling uses the ? operator with appropriate CliError mappings.


40-42: Do not gate these constants.

ARG_OSM_PBF, ARG_WIKIDATA_DUMP, and ARG_OUTPUT_DIR are used within the #[arg(...)] attributes of the IngestArgs struct definition (lines 156, 160, 164). The IngestArgs struct is part of the ungated Command enum and must be parsable regardless of whether the store-sqlite feature is enabled. Gating the constants would cause a compile error when the feature is disabled, as the attributes would reference undefined constants at compile time.

Likely an incorrect or invalid review comment.

docs/wildside-engine-design/06-implementation-testing-deployment.md (1)

1-167: Documentation structure is comprehensive.

The implementation, testing, and deployment strategy document covers packaging, versioning, feature flags, testing disciplines (golden tests, property tests, benchmarks, fuzz testing, API contract tests), and migration strategy. The tables are well-formatted and provide clear rationale for design decisions.

wildside-core/src/store/spatial_index.rs (3)

97-126: Read/write format consistency verified.

The header (magic + version) is now written as raw bytes separately from the bincode payload, matching the read logic in load_index_entries. This addresses the prior critical review feedback about format mismatch.


168-301: Comprehensive test coverage for spatial index persistence.

The test suite covers:

  • Round-trip serialization/deserialization
  • Missing file I/O errors
  • Invalid magic header detection
  • Decode failure handling
  • Unsupported and legacy version rejection
  • Payload inspection after write

The use of rstest fixtures (temp_index_path, sample_pois) follows project conventions.


23-60: Error types are well-designed.

SpatialIndexError and SpatialIndexWriteError provide semantic variants with contextual paths and source errors. The #[source] attribute enables proper error chaining.

wildside-cli/src/tests/helpers.rs (4)

128-135: let-else pattern is appropriate.

The merge_field function uses if let with let Some(value) binding, which is valid Rust 2024 syntax. The logic correctly prioritises env over file layer values.


38-102: DatasetFiles provides clean test fixture management.

The struct manages TempDir lifetime correctly via _dir field. The helper methods expose paths without ownership transfer, enabling reuse across test scenarios.


166-175: Hardcoded Wikidata payload is adequate for unit tests.

The minimal JSON payload contains sufficient structure to exercise claim extraction without requiring external fixtures. Mark as test data if this grows to warrant extraction.


145-148: Remove this suggestion. The fixture directory path is compile-time validated through the workspace configuration in Cargo.toml, which declares wildside-data as a workspace member. The env!("CARGO_MANIFEST_DIR") macro guarantees the path resolution at compile time, not runtime, making it inherently safe. No additional checks or documentation are necessary.

Likely an incorrect or invalid review comment.

wildside-core/src/store/sqlite.rs (7)

1-21: LGTM: Module documentation and imports are well-structured.

The module-level doc comment clearly describes the purpose, imports are organized logically, and the SQLITE_MAX_VARIABLE_NUMBER constant includes helpful context about the chunking strategy.


23-56: LGTM: Error types follow best practices.

The SqlitePoiStoreError enum provides semantic error variants with clear documentation. Proper use of #[source] attributes enables error chaining, and the structured variants allow callers to inspect and handle specific failure modes.


58-69: LGTM: Custom Debug implementation is appropriate.

The custom Debug implementation shows the index size rather than materializing the entire RTree, which is sensible for a large spatial index. The use of finish_non_exhaustive() correctly indicates that internal details are omitted.


98-115: LGTM: Spatial query implementation is correct.

The bbox query correctly converts from geo::Rect to rstar::AABB, queries the spatial index, sorts results by id for deterministic ordering, and returns a boxed iterator. Materializing all results into a Vec is consistent with the in-memory design of this store.


156-159: LGTM: Variable limit function is correctly structured.

The function is designed for future extensibility (e.g., querying SQLite's runtime limit), so the unused connection parameter and placeholder comment are appropriate.


161-190: LGTM: POI loading logic is correct and safe.

The dynamic SQL construction on line 170 is safe because placeholders are derived from ids.len(), not user input. Parameter binding via params_from_iter prevents SQL injection. JSON tag parsing includes proper error handling with InvalidTags, and results are sorted for deterministic behaviour.


192-365: LGTM: Comprehensive test coverage with appropriate fixture design.

The test suite covers both successful queries (bbox retrieval, sorting, empty results) and error conditions (missing POI, corrupted magic, unsupported version, invalid tags). Proper use of rstest fixtures with composition (e.g., sqlite_store_fixture depends on temp_artifacts and sample_pois) promotes reusability. The use of .expect() in tests follows the project's guidelines.

docs/wildside-engine-design/02-data-foundation.md (5)

1-67: LGTM: Introduction and parser analysis are well-structured.

The opening section clearly establishes the importance of the data foundation, and the comparative analysis of OSM PBF parsers is thorough and well-reasoned. The recommendation for osmpbf based on licensing and technical merits is sound.


68-88: LGTM: Ingestion summary accurately describes the implementation.

The section clearly explains the OsmIngestReport functionality, including the POI emission rules, coordinate indexing strategy, and handling of unresolved geometry. The technical details align with the implementation in the codebase.


90-213: LGTM: Wikidata ETL design is comprehensive and well-documented.

The sections clearly describe the dump acquisition implementation, linked entity extraction, and processing flow. The Mermaid sequence diagram effectively illustrates the line-by-line parsing logic with error handling. The technical approach is sound and aligns with the implemented functionality.


214-250: LGTM: Schema design and comparison are well-documented.

The SQLite schema section clearly describes the table structure, indexing strategy, and versioning approach. The comparative analysis table effectively contrasts live SPARQL queries with offline dump processing, providing clear justification for the architectural decision.


251-280: LGTM: Geospatial library recommendations are well-justified.

The section clearly explains the rationale for adopting the GeoRust ecosystem, specifically geo for geometric types and rstar for spatial indexing. The recommendation aligns with the implementation choices made throughout the codebase.

wildside-cli/src/solve.rs (6)

8-24: LGTM: Feature-gated imports are correctly structured.

The imports are properly gated behind their respective features. The store-sqlite feature gates the store, travel-time provider, and scorer. Solver imports are correctly gated with VrpSolver taking priority when both solver features are enabled.


31-66: LGTM: Type aliases centralize solver selection logic.

The type aliases and constants effectively centralize the feature-based solver selection. The precedence order (VRP over OR-Tools) is clear and consistent. The test-only SelectedSolverKind enum and SELECTED_SOLVER_KIND constant provide a clean way to verify feature flag behaviour in tests.


197-202: LGTM: Builder delegates to centralized helpers.

The DefaultSolveSolverBuilder implementation now cleanly delegates to make_store_and_deps and build_solver_with_features, eliminating the duplicated feature-gate logic that was present in the previous version. This aligns with the past review feedback.


210-235: LGTM: Store dependency construction is correctly gated.

The make_store_and_deps helper properly constructs the store, scorer, and provider when store-sqlite is enabled, and returns a clear MissingFeature error otherwise. The unused variable suppression on line 229 correctly prevents warnings in the disabled feature path.


237-262: LGTM: Solver construction centralizes feature selection.

The build_solver_with_features helper uses the SelectedSolver type alias to construct the appropriate solver based on enabled features. The two-level feature gating (store-sqlite outer, solver inner) ensures clear error messages for missing features. Unused variable suppressions on lines 247 and 256 are appropriate.


336-353: LGTM: Feature flag test validates solver selection logic.

The test verifies that the compile-time SELECTED_SOLVER_KIND constant matches the expected value based on enabled features. This effectively validates that the type alias selection logic is consistent with the feature flags, addressing the past review feedback about testing the selection logic.

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml Outdated
Comment thread docs/wildside-engine-design/01-introduction.md Outdated
Comment thread docs/wildside-engine-design/02-data-foundation.md Outdated
Comment thread docs/wildside-engine-design/02-data-foundation.md Outdated
Comment thread docs/wildside-engine-design/06-implementation-testing-deployment.md Outdated
Comment thread Makefile
Comment thread wildside-core/src/store/sqlite.rs Outdated
Comment thread wildside-core/src/store/sqlite.rs Outdated
Comment thread wildside-core/src/store/sqlite.rs
- Use path references instead of cloned PathBufs for database and index paths
- Replace debug_assert with unreachable! in find_missing_poi_in_chunk
- Minor code clarity and efficiency improvements

Additionally, enable conditional compilation of IngestOutcome struct based on 'store-sqlite' feature, update Cargo.toml dependencies, and adjust CI test flags to include 'solver-vrp' and 'store-sqlite' features in default-features matrix.

Minor documentation typo fixes (normalise -> normalize, parameterised -> parameterized, normalized consistent) included.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Phase 4: Implement root engine facade and feature flags Phase 4: Decompose oversized design document Jan 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Fix all issues with AI Agents 🤖
In @.github/workflows/ci.yml:
- Around line 91-101: The CI workflow duplicates the checkout, Rust toolchain,
cache, and cargo-nextest steps (the uses entries for actions/checkout,
dtolnay/rust-toolchain, Swatinem/rust-cache, and taiki-e/install-action for
cargo-nextest) between jobs; either document the intentional duplication in a
comment near those steps explaining the trade-off (small stable setup vs.
complexity of composite actions) or extract these repeated steps into a reusable
composite action or a reusable workflow and replace the duplicated blocks with a
single reusable call to reduce maintenance burden.
- Around line 69-91: The feature-matrix job runs unconditionally; add a
dependency so it only runs after the build job succeeds by inserting needs:
build under the feature-matrix job (i.e., add needs: build to the feature-matrix
job definition just before the existing strategy: block) so the feature-matrix
job is gated on the build job completing successfully.
- Around line 75-76: The matrix entry named "default-features" is misleading
because test_flags uses "--no-default-features --features
solver-vrp,store-sqlite"; rename the matrix key "default-features" to
"explicit-defaults" (or "default-explicit") to reflect that features are being
specified explicitly, and update any workflow references that consume this
matrix value to use the new name; ensure the test_flags line remains unchanged.

In @docs/wildside-engine-design/02-data-foundation.md:
- Around line 271-273: Remove the erroneous blank line splitting the sentence
that begins "It is designed to work seamlessly with the types from the" so that
it reads continuously with the following "`geo` crate." — ensure the phrase
flows into the next clause about building an `rstar::RTree` without a paragraph
break.
- Around line 30-32: Remove the stray blank line splitting the sentence: join "a
design that" and "`osmpbf` leverages to process these blobs in parallel across
multiple CPU" into a single continuous sentence so the phrase reads without the
paragraph break (edit the sentence containing "a design that" and the following
line starting with "`osmpbf`").
- Around line 100-101: Replace the incorrect "task.5." sequence with proper
markdown footnote syntax and a single terminal period: change the fragment so
the sentence reads with the `wikidata-rust` crate referenced and the footnote as
`[^5]`, e.g. "... The `wikidata-rust` crate is a purpose-built tool for this
task[^5]." Ensure only one period appears at the end and keep the
`wikidata-rust` identifier unchanged.

In @docs/wildside-engine-design/05-solver-implementation.md:
- Around line 84-86: The phrase "absolutely need" in the sentence that reads
"consumers of the Wildside engine can choose to opt into this complexity only if
they absolutely need it" is flagged for being overused; replace it with a
stronger, more precise alternative (e.g., "require it," "have a clear need for
it," or "must use it") in the same paragraph so the sentence instead reads that
consumers can opt into the feature-flagged implementation only if they require
it or have a clear need, preserving meaning but avoiding the cliché.
- Around line 4-6: Replace the first-person phrase "allows us to abstract the
solver behind a trait" with a neutral phrasing such as "allows abstraction of
the solver behind a trait" or "enables the solver to be abstracted behind a
trait" so the sentence no longer uses "us" and complies with documentation
guidelines; ensure the rest of the sentence ("making the specific implementation
a configurable choice") remains unchanged.
- Around line 98-101: The paragraph continuation immediately follows the
code-formatted signature `fn get_travel_time_matrix(&self, pois:
&[PointOfInterest]) -> Result<TravelTimeMatrix, TravelTimeError>` without a
separating blank line; insert a single blank line after that code line (or
alternatively merge the continuation sentence into the same paragraph) so the
Markdown renders correctly and the sentence "Keeping the solver synchronous
preserves object safety and makes the core embeddable." is separated from the
code block.
- Around line 24-37: Replace the bare numeric footnotes "13", "15", and "16"
with GitHub-flavoured footnote references [^13], [^15], and [^16] wherever they
appear, and then either add matching footnote definitions at the end of this
document or convert those references into explicit cross-references to the
existing definitions in the users-guide (so the tokens resolve); ensure the
added references use the exact labels [^13], [^15], [^16] to match the
replacements.

Caution

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

⚠️ Outside diff range comments (2)
wildside-cli/src/lib.rs (2)

232-240: Error conflation may obscure root cause.

require_existing treats Err(_) identically to Ok(false), discarding the underlying error. Permission failures, symlink issues, or I/O errors will present as "missing file" to the user, hindering diagnosis.

Proposed fix preserving the original error
     fn require_existing(path: &Utf8Path, field: &'static str) -> Result<(), CliError> {
         match wildside_fs::file_is_file(path) {
             Ok(true) => Ok(()),
-            Ok(false) | Err(_) => Err(CliError::MissingSourceFile {
+            Ok(false) => Err(CliError::MissingSourceFile {
                 field,
                 path: path.to_path_buf(),
             }),
+            Err(e) => Err(CliError::SourceFileAccess {
+                field,
+                path: path.to_path_buf(),
+                source: e,
+            }),
         }
     }

This requires adding a SourceFileAccess variant to CliError. If the current behaviour is intentional (treating inaccessible as missing), add a code comment explaining the rationale.


265-276: Type alias to () maintains API surface when feature disabled.

Using type IngestOutcome = (); when store-sqlite is absent is a pragmatic approach to keep the function signature consistent across feature permutations. Document this pattern in a comment so future maintainers understand the intent.

Proposed documentation
+/// When `store-sqlite` is disabled, `IngestOutcome` is a unit type because
+/// ingestion always fails with `MissingFeature`. This preserves the function
+/// signature across feature permutations.
 #[cfg(not(feature = "store-sqlite"))]
 type IngestOutcome = ();
♻️ Duplicate comments (1)
docs/wildside-engine-design/01-introduction.md (1)

66-67: Add comma before "so" to separate independent clauses.

Line 66–67 reads: "...directly in the R*-tree so lookups can avoid secondary hash-map probes." These are two independent clauses (main clause + result clause) joined by "so", requiring a comma between them.

- the full POI records (id, `geo::Coord`, and tag map) directly in the R\*-tree
+ the full POI records (id, `geo::Coord`, and tag map) directly in the R\*-tree,
  so lookups can avoid secondary hash-map probes. During start-up, the store
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76a91f7 and 896962a.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • Cargo.toml
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • wildside-cli/src/lib.rs
  • wildside-core/src/store/sqlite.rs
🧰 Additional context used
📓 Path-based instructions (6)
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
When new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve, proactively update the relevant file(s) in the docs/ directory to reflect the latest state. Ensure the documentation remains accurate and current.
Documentation must use en-GB-oxendict spelling and grammar ("-ize" / "-yse" / "-our") with the exception of the LICENSE filename.

docs/**/*.md: Use British English based on the Oxford English Dictionary locale en-GB, including -ize suffixes (e.g., realize, organization), -lyse suffixes (e.g., analyse, paralyse), -our suffixes (e.g., colour, behaviour), -re suffixes (e.g., centre, calibre), double 'l' (e.g., cancelled, counsellor), maintained 'e' (e.g., likeable, rateable), -ogue suffixes (e.g., analogue, catalogue)
Use the Oxford comma in documentation: 'ships, planes, and hovercraft' where it aids comprehension
Treat company names as collective nouns, for example: 'Concordat Industries are expanding'
Write headings in sentence case
Use Markdown headings (#, ##, ###, and so on) in order without skipping levels
Follow markdownlint recommendations
Provide code blocks and lists using standard Markdown syntax
Always provide a language identifier for fenced code blocks; use 'plaintext' for non-code text
Use '-' as the first level bullet and renumber lists when items change
Prefer inline links using 'text' or angle brackets around the URL
Ensure blank lines before and after bulleted lists and fenced blocks
Ensure tables have a delimiter line below the header row
Expand any uncommon acronym on first use, for example, Continuous Integration (CI)
Wrap paragraphs at 80 columns
Do not wrap tables
Use GitHub-flavoured numeric footnotes referenced as '[^1]'
Footnotes must be numbered in order of appearance in the document
Caption ev...

Files:

  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Markdown tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
docs/**/!(README).md

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Avoid first and second person personal pronouns outside the README.md file

Files:

  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
docs/**/*.{md,rs}

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Wrap code at 120 columns

Files:

  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make check-fmt, make lint, and make test before committing in Rust projects.
make check-fmt executes cargo fmt --workspace -- --check to validate formatting across the entire workspace without modifying files.
make lint executes cargo clippy --workspace --all-targets --all-features -- -D warnings to lint every target with all features enabled and deny all Clippy warnings.
make test executes cargo test --workspace to run the full workspace test suite.
Clippy warnings MUST be disallowed in Rust code.
Fix any warnings emitted during tests in the Rust code itself rather than silencing them.
Where a Rust function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a Rust function has too many parameters, group related parameters in meaningfully named structs.
Where a Rust function is returning a large error, consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality in Rust. Run both before and after making any change.
Every Rust module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings in Rust.
Handle errors with the Result type instead of panicking in Rust where feasible.
Avoid unsafe code in Rust unless absolutely necessary, and document any usage clearly.
Place function attributes after doc comments in Rust.
Do not use return in single-line Rust functions.
Use predicate functions for conditional criteria in Rust with more than two branches.
Rust lints must not be silenced except as a last resort.
Rust lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow in Rust.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated Rust tests with `#[rstest(...)...

Files:

  • wildside-cli/src/lib.rs
  • wildside-core/src/store/sqlite.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • wildside-cli/src/lib.rs
  • wildside-core/src/store/sqlite.rs
Cargo.toml

📄 CodeRabbit inference engine (AGENTS.md)

Cargo.toml: Use explicit version ranges in Cargo.toml and keep dependencies up-to-date.
All crate versions specified in Cargo.toml must use SemVer-compatible caret requirements (e.g., some-crate = "1.2.3"). Wildcard (*) or open-ended inequality (>=) version requirements are strictly forbidden.
Tilde requirements (~) in Rust dependencies should only be used where a dependency must be locked to patch-level updates for a specific, documented reason.

Files:

  • Cargo.toml
🧠 Learnings (2)
📚 Learning: 2026-01-03T14:22:19.441Z
Learnt from: leynos
Repo: leynos/wildside-engine PR: 81
File: docs/wildside-engine-design/02-data-foundation.md:0-0
Timestamp: 2026-01-03T14:22:19.441Z
Learning: Apply en-GB Oxford English spelling in Markdown files by preferring -ize (e.g., normalize, initialize, organize) over -ise, -yse over -yze (e.g., analyse, paralyse), and -our over -or (e.g., colour, favour). The Oxford British English convention favors -ize in these contexts. This guidance should be applied across Markdown documentation in the docs directory to maintain consistent spelling.

Applied to files:

  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/06-implementation-testing-deployment.md
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
📚 Learning: 2026-01-04T04:50:10.691Z
Learnt from: leynos
Repo: leynos/rstest-bdd PR: 384
File: crates/rstest-bdd/tests/async_step_infrastructure.rs:4-4
Timestamp: 2026-01-04T04:50:10.691Z
Learning: In en-GB-oxendict (Oxford English) spelling convention, verbs use the -ize ending (e.g., "normalizes," "organizes," "realizes"), not -ise. The convention is specifically -ize / -yse / -our, so "normalizes" is correct in rstest-bdd documentation and comments.

Applied to files:

  • docs/wildside-engine-design/02-data-foundation.md
🪛 LanguageTool
docs/wildside-engine-design/05-solver-implementation.md

[style] ~85-~85: The term ‘absolutely need’ is used quite frequently. Consider using a less common alternative to set your writing apart from others.
Context: ...o opt into this complexity only if they absolutely need it, without burdening the default setup...

(ABSOLUTELY_GREAT_RULE)

docs/wildside-engine-design/06-implementation-testing-deployment.md

[style] ~69-~69: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ed as a JSON file containing a complete problem instance and its expected solution. The...

(EN_REPEATEDWORDS_PROBLEM)

docs/wildside-engine-design/01-introduction.md

[style] ~16-~16: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ithms, and solving NP-hard optimization problems all align with the Rust ecosystem's str...

(EN_REPEATEDWORDS_PROBLEM)


[uncategorized] ~65-~65: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...`, and tag map) directly in the R*-tree so lookups can avoid secondary hash-map pr...

(COMMA_COMPOUND_SENTENCE_2)

docs/wildside-engine-design/02-data-foundation.md

[typographical] ~1-~1: To join two clauses or introduce examples, consider using an em dash.
Context: # Section 1: The Data Foundation - Ingesting and Integrating Open Data The...

(DASH_RULE)


[grammar] ~30-~30: Please add a punctuation mark at the end of paragraph.
Context: ...quence of independent "blobs," a design that osmpbf leverages to process these bl...

(PUNCTUATION_PARAGRAPH_END)


[style] ~45-~45: This expression can be considered offensive.
Context: ...rate is licensed under the "Do What The Fuck You Want To Public Licence, Version 2" (WTF...

(PROFANITY)


[uncategorized] ~123-~123: The preposition ‘to’ seems more likely in this position.
Context: ...ous reqwest client, issues requests for the Tokio runtime, and always sends a d...

(AI_HYDRA_LEO_REPLACE_FOR_TO)


[style] ~161-~161: You have used the passive voice repeatedly in nearby sentences. To make your writing clearer and easier to read, consider using active voice.
Context: ... referenced by the PoiEntityLinks set are processed further. For those entities, the parser...

(REP_PASSIVE_VOICE)


[style] ~218-~218: Would you like to use the Oxford spelling “initialize”? The spelling ‘initialise’ is also correct.
Context: ...Schema initialization is handled by the initialise_schema function, which enables foreign...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~242-~242: Possible missing preposition found.
Context: ... thousands of queries per user request. Will be rate-limited and result in extreme l...

(AI_HYDRA_LEO_MISSING_IT)


[grammar] ~271-~271: Please add a punctuation mark at the end of paragraph.
Context: ... to work seamlessly with the types from the geo crate. The practical implementat...

(PUNCTUATION_PARAGRAPH_END)

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (26)
docs/wildside-engine-design/06-implementation-testing-deployment.md (1)

1-166: Approve file with notation on static analysis.

All past review comments have been correctly addressed:

  • British spelling ("licence," "parameterized," "artefacts," "behavioural") ✓
  • Comma before "so" at line 19 ✓
  • Bold formatting for feature flags, schema fields, and infrastructure components ✓
  • Redundant phrasing removed ("a separate repository" at line 144) ✓
  • Extraneous "its" removed ✓

The file follows en-GB-oxendict conventions, maintains 80-column paragraph wrapping, and properly formats tables, headings, and lists. Content aligns with PR Phase 4 objectives and provides clear guidance on testing discipline, deployment strategy, and repository organisation.

Note on static analysis: LanguageTool flags line 69 for word repetition ("expected"), but this is a false positive—the uses at line 70 ("expected solution") and line 77 ("expected route") are semantically distinct and separated by multiple lines.

wildside-core/src/store/sqlite.rs (12)

1-17: LGTM!

Module doc comment is present per guidelines, and imports are appropriate for the functionality.


19-21: LGTM!

The SQLite variable limit constant is correctly documented and uses the well-known default value.


23-56: LGTM!

The error enum is well-structured with semantic variants, proper thiserror derivation, and thorough documentation for each variant.


58-69: LGTM!

The struct is appropriately documented, and the custom Debug implementation avoids dumping the entire index whilst indicating additional hidden state.


71-96: LGTM!

The open method correctly uses path references, maps errors appropriately, and validates index integrity against the SQLite database before bulk-loading the in-memory RTree.


98-115: LGTM!

The PoiStore trait implementation correctly queries the in-memory RTree, clones results as required, and sorts for deterministic output order.


117-129: LGTM!

The helper correctly uses unreachable! for the invariant violation, and the binary search logic is sound given that load_pois_chunk guarantees sorted output.


131-152: LGTM!

The function correctly handles empty inputs, deduplicates IDs, and respects SQLite's parameter limit through chunking.


154-157: LGTM!

The helper returns the safe default limit. Retaining the connection parameter for future tunability is a reasonable forward-looking pattern.


159-188: LGTM!

The function safely constructs parameterised queries, properly maps JSON parsing errors to domain errors, and sorts results for downstream binary search.


190-231: LGTM!

Test fixtures follow rstest patterns per coding guidelines, and the helper function provides clean test data construction.


233-363: LGTM!

Test coverage is comprehensive, covering happy paths (bbox queries, sorting), edge cases (empty results), and all error variants (missing POI, corrupted magic, unsupported version, invalid tags). The tests correctly use expect() as permitted in test code per coding guidelines.

Cargo.toml (7)

1-8: LGTM!

Package metadata correctly inherits workspace fields and disables publishing. This is a clean facade crate setup.


10-14: LGTM!

Optional workspace dependencies for solver backends and explicit default-features = false on the core crate enable proper feature-flag composition.


23-25: LGTM!

Workspace lint inheritance keeps the facade crate consistent with the rest of the workspace.


34-34: LGTM!

New wildside-solver-ortools member correctly added to the workspace.


49-55: Workspace dependencies comply with SemVer caret requirements. All versions in this segment use proper explicit version ranges as mandated by the coding guidelines. The rstest-bdd and rstest-bdd-macros 0.3.0 releases are published.


21-21: No action required. wildside-solver-ortools does not expose a test-support feature, so the conditional concern does not apply. The current implementation correctly enables test-support only for the crates that provide it.


38-42: No action required.

The rust-version = "1.92" is the correct, current stable Rust version (released December 11, 2025) and is appropriate for the edition = "2024" specification. The configuration is up-to-date and valid.

docs/wildside-engine-design/05-solver-implementation.md (1)

1-149: Documentation structure and content are sound.

The solver implementation design is well-structured, covering the trait interface, native Rust and OR-Tools backends, travel-time provider boundary, and implementation details. The technical content aligns with the feature-flag architecture described in the PR objectives.

wildside-cli/src/lib.rs (4)

69-83: Feature-gated ingestion flow is correctly implemented.

The #[cfg] blocks ensure exactly one code path compiles based on the store-sqlite feature. The let _ = args; pattern correctly silences the unused variable warning when the feature is disabled.


4-26: Feature-gated imports are well-organised.

The conditional imports correctly align with their usage sites, avoiding dead-code warnings when store-sqlite is disabled. This follows the coding guideline to conditionally compile feature-specific code.


140-158: Wikidata dump handling is robust.

open_wikidata_dump correctly detects .bz2 extension case-insensitively and wraps the file in MultiBzDecoder as appropriate. The BufReader wrapper improves I/O efficiency for both compressed and uncompressed paths.


243-263: TryFrom implementation correctly validates required fields.

The implementation uses ok_or to convert Option to Result with descriptive MissingArgument errors, and sensibly defaults output_dir to the current directory. Error propagation follows guidelines.

.github/workflows/ci.yml (1)

69-105: No SQLite system dependency installation required.

The codebase already correctly configures rusqlite with the bundled feature across all packages (wildside-core, wildside-cli, wildside-scorer, and wildside-data). The bundled feature statically links SQLite, eliminating the need for system packages like libsqlite3-dev. The CI workflow requires no changes.

Likely an incorrect or invalid review comment.

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml
Comment thread docs/wildside-engine-design/02-data-foundation.md Outdated
Comment thread docs/wildside-engine-design/02-data-foundation.md Outdated
Comment thread docs/wildside-engine-design/02-data-foundation.md Outdated
Comment thread docs/wildside-engine-design/05-solver-implementation.md
Comment thread docs/wildside-engine-design/05-solver-implementation.md
Comment thread docs/wildside-engine-design/05-solver-implementation.md
Comment thread docs/wildside-engine-design/05-solver-implementation.md
Introduced `SourceFileAccess` error for IO errors encountered while accessing input paths in the CLI ingestion flow. Updated `require_existing` to distinguish between missing files and other IO errors, returning the new error variant accordingly. Also includes minor docs formatting fixes.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Phase 4: Decompose oversized design document Phase 4: Feature flags and root engine facade Jan 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Fix all issues with AI Agents 🤖
In @docs/wildside-engine-design/01-introduction.md:
- Around line 1-188: The document contains a dangling footnote marker
"computationally demanding.1" (the inline marker intended as [^1]) but lacks the
corresponding footnote definition; add a footnote definition line `[^1]: Rust's
performance, memory-safety, and concurrency guarantees make it particularly
well-suited for data-intensive, concurrent workloads such as parsing large
binary files and running complex algorithms.` at the end of the file so the
reference resolves and follows GitHub-flavoured Markdown footnote formatting.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 896962a and 5899bb1.

📒 Files selected for processing (3)
  • docs/wildside-engine-design/01-introduction.md
  • wildside-cli/src/error.rs
  • wildside-cli/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (5)
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
When new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve, proactively update the relevant file(s) in the docs/ directory to reflect the latest state. Ensure the documentation remains accurate and current.
Documentation must use en-GB-oxendict spelling and grammar ("-ize" / "-yse" / "-our") with the exception of the LICENSE filename.

docs/**/*.md: Use British English based on the Oxford English Dictionary locale en-GB, including -ize suffixes (e.g., realize, organization), -lyse suffixes (e.g., analyse, paralyse), -our suffixes (e.g., colour, behaviour), -re suffixes (e.g., centre, calibre), double 'l' (e.g., cancelled, counsellor), maintained 'e' (e.g., likeable, rateable), -ogue suffixes (e.g., analogue, catalogue)
Use the Oxford comma in documentation: 'ships, planes, and hovercraft' where it aids comprehension
Treat company names as collective nouns, for example: 'Concordat Industries are expanding'
Write headings in sentence case
Use Markdown headings (#, ##, ###, and so on) in order without skipping levels
Follow markdownlint recommendations
Provide code blocks and lists using standard Markdown syntax
Always provide a language identifier for fenced code blocks; use 'plaintext' for non-code text
Use '-' as the first level bullet and renumber lists when items change
Prefer inline links using 'text' or angle brackets around the URL
Ensure blank lines before and after bulleted lists and fenced blocks
Ensure tables have a delimiter line below the header row
Expand any uncommon acronym on first use, for example, Continuous Integration (CI)
Wrap paragraphs at 80 columns
Do not wrap tables
Use GitHub-flavoured numeric footnotes referenced as '[^1]'
Footnotes must be numbered in order of appearance in the document
Caption ev...

Files:

  • docs/wildside-engine-design/01-introduction.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Markdown tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/wildside-engine-design/01-introduction.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/wildside-engine-design/01-introduction.md
docs/**/!(README).md

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Avoid first and second person personal pronouns outside the README.md file

Files:

  • docs/wildside-engine-design/01-introduction.md
docs/**/*.{md,rs}

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Wrap code at 120 columns

Files:

  • docs/wildside-engine-design/01-introduction.md
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make check-fmt, make lint, and make test before committing in Rust projects.
make check-fmt executes cargo fmt --workspace -- --check to validate formatting across the entire workspace without modifying files.
make lint executes cargo clippy --workspace --all-targets --all-features -- -D warnings to lint every target with all features enabled and deny all Clippy warnings.
make test executes cargo test --workspace to run the full workspace test suite.
Clippy warnings MUST be disallowed in Rust code.
Fix any warnings emitted during tests in the Rust code itself rather than silencing them.
Where a Rust function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a Rust function has too many parameters, group related parameters in meaningfully named structs.
Where a Rust function is returning a large error, consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality in Rust. Run both before and after making any change.
Every Rust module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings in Rust.
Handle errors with the Result type instead of panicking in Rust where feasible.
Avoid unsafe code in Rust unless absolutely necessary, and document any usage clearly.
Place function attributes after doc comments in Rust.
Do not use return in single-line Rust functions.
Use predicate functions for conditional criteria in Rust with more than two branches.
Rust lints must not be silenced except as a last resort.
Rust lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow in Rust.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated Rust tests with `#[rstest(...)...

Files:

  • wildside-cli/src/lib.rs
  • wildside-cli/src/error.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • wildside-cli/src/lib.rs
  • wildside-cli/src/error.rs
🧠 Learnings (1)
📚 Learning: 2026-01-03T14:22:19.441Z
Learnt from: leynos
Repo: leynos/wildside-engine PR: 81
File: docs/wildside-engine-design/02-data-foundation.md:0-0
Timestamp: 2026-01-03T14:22:19.441Z
Learning: Apply en-GB Oxford English spelling in Markdown files by preferring -ize (e.g., normalize, initialize, organize) over -ise, -yse over -yze (e.g., analyse, paralyse), and -our over -or (e.g., colour, favour). The Oxford British English convention favors -ize in these contexts. This guidance should be applied across Markdown documentation in the docs directory to maintain consistent spelling.

Applied to files:

  • docs/wildside-engine-design/01-introduction.md
🧬 Code graph analysis (1)
wildside-cli/src/lib.rs (4)
wildside-core/src/store/spatial_index.rs (1)
  • write_spatial_index (90-95)
wildside-data/src/wikidata/store/persistence.rs (1)
  • persist_claims_to_path (285-295)
wildside-data/src/ingest/mod.rs (1)
  • ingest_osm_pbf_report (149-202)
wildside-data/src/ingest/sqlite.rs (1)
  • persist_pois_to_sqlite (97-122)
🪛 LanguageTool
docs/wildside-engine-design/01-introduction.md

[style] ~16-~16: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ithms, and solving NP-hard optimization problems all align with the Rust ecosystem's str...

(EN_REPEATEDWORDS_PROBLEM)

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (19)
wildside-cli/src/lib.rs (14)

4-26: LGTM: Import gating is correct.

The conditional compilation correctly gates all ingestion implementation dependencies behind store-sqlite whilst keeping Utf8PathBuf unconditional for the always-available IngestArgs struct.


43-46: LGTM: Environment constants correctly gated.

The constants are only used within the feature-gated TryFrom<IngestArgs> implementation.


69-83: LGTM: Feature gating pattern is correct.

The dual implementation correctly returns MissingFeature when store-sqlite is disabled whilst preserving the function signature for consistent CLI dispatch.


85-90: LGTM: Configuration resolution is clean.


127-138: LGTM: Wikidata claims extraction is correct.

The early return for empty links avoids unnecessary processing.


140-151: LGTM: Wikidata dump opening handles BZ2 decompression.


153-158: LGTM: Extension check is correct.


204-209: LGTM: Configuration merging follows OrthoConfig pattern.


211-217: LGTM: IngestConfig struct is well-defined.


232-251: LGTM: File existence validation correctly distinguishes error cases.

The three-way error handling (file exists, file missing, IO error) aligns with the new SourceFileAccess variant.


254-274: LGTM: TryFrom conversion handles missing arguments correctly.


276-284: LGTM: IngestOutcome captures ingestion results.


286-290: LGTM: Unit type preserves signature across feature permutations.

The phantom unit type is a pragmatic solution for maintaining a consistent return type when ingestion always fails with MissingFeature.


92-125: Add parent directory creation to write_spatial_index.

The write_index function directly calls File::create(path) without ensuring the parent directory exists. This differs from persist_pois_to_sqlite, which calls ensure_parent_dir(path)? before opening the connection. Update write_index to call ensure_parent_dir before file creation to prevent failures when output_dir lacks the required hierarchy.

wildside-cli/src/error.rs (5)

12-13: LGTM: Import gating matches usage.

The SpatialIndexWriteError import is consistently gated with its usage in the WriteSpatialIndex variant.


35-40: LGTM: MissingFeature variant is well-designed.

The variant correctly uses static string slices and provides clear error messages for missing compile-time features.


53-60: LGTM: SourceFileAccess variant correctly distinguishes IO errors.

The new variant properly separates IO errors from missing file errors, as described in the commit message.


100-106: LGTM: WriteSpatialIndex variant correctly feature-gated.

The variant is only available when store-sqlite is enabled, matching the availability of the underlying error type.


129-131: LGTM: OpenPoiStore variant correctly feature-gated.

The transparent delegation to SqlitePoiStoreError is appropriate and matches the feature availability.

Comment thread docs/wildside-engine-design/01-introduction.md
…otation to import

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Phase 4: Feature flags and root engine facade Phase 4: Feature flags, root engine facade, and doc decomposition Jan 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Fix all issues with AI Agents 🤖
In @wildside-cli/src/lib.rs:
- Around line 70-84: The unused args suppression should use a narrow #[expect]
on the parameter instead of a blanket let _ = args;—update the run_ingest
signature (parameter name args in fn run_ingest(args: IngestArgs)) to apply
#[expect(unused_variables, reason = "feature-gated; preserved for when
store-sqlite is enabled")] to that parameter and remove the let _ = args; line
so the lint is explicitly documented and scoped.

Caution

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

⚠️ Outside diff range comments (1)
wildside-cli/src/lib.rs (1)

56-56: Add doc comment to public function.

Document pub fn run() with a /// doc comment. The guidelines require public APIs to use Rustdoc comments.

Suggested addition
+/// Run the Wildside CLI with the current process arguments and environment.
+///
+/// Parses CLI arguments and dispatches to the appropriate subcommand handler.
+/// Returns a [`CliError`] if argument parsing fails or the subcommand encounters an error.
 pub fn run() -> Result<(), CliError> {

As per coding guidelines: "Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc."

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5899bb1 and cb857b8.

📒 Files selected for processing (1)
  • wildside-cli/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make check-fmt, make lint, and make test before committing in Rust projects.
make check-fmt executes cargo fmt --workspace -- --check to validate formatting across the entire workspace without modifying files.
make lint executes cargo clippy --workspace --all-targets --all-features -- -D warnings to lint every target with all features enabled and deny all Clippy warnings.
make test executes cargo test --workspace to run the full workspace test suite.
Clippy warnings MUST be disallowed in Rust code.
Fix any warnings emitted during tests in the Rust code itself rather than silencing them.
Where a Rust function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a Rust function has too many parameters, group related parameters in meaningfully named structs.
Where a Rust function is returning a large error, consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality in Rust. Run both before and after making any change.
Every Rust module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings in Rust.
Handle errors with the Result type instead of panicking in Rust where feasible.
Avoid unsafe code in Rust unless absolutely necessary, and document any usage clearly.
Place function attributes after doc comments in Rust.
Do not use return in single-line Rust functions.
Use predicate functions for conditional criteria in Rust with more than two branches.
Rust lints must not be silenced except as a last resort.
Rust lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow in Rust.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated Rust tests with `#[rstest(...)...

Files:

  • wildside-cli/src/lib.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • wildside-cli/src/lib.rs
🧬 Code graph analysis (1)
wildside-cli/src/lib.rs (5)
wildside-core/src/store/spatial_index.rs (1)
  • write_spatial_index (90-95)
wildside-data/src/wikidata/etl/mod.rs (1)
  • extract_linked_entity_claims (153-198)
wildside-data/src/wikidata/store/persistence.rs (1)
  • persist_claims_to_path (285-295)
wildside-data/src/ingest/mod.rs (1)
  • ingest_osm_pbf_report (149-202)
wildside-data/src/ingest/sqlite.rs (1)
  • persist_pois_to_sqlite (97-122)
🔍 Remote MCP

The web search didn't find the specific Wildside project. Let me search more specifically for Wildside and feature-flagging patterns:Excellent! Now let me search for information about testing strategies with conditional features:## Summary of Additional Context Found

Based on my search findings, here is relevant context that would help in reviewing this PR:

Rust Feature Flagging Best Practices

Conditional compilation in Rust is determined statically during compilation, allowing code to be compiled only under certain conditions using #[cfg] and #[cfg_attr] attributes. The PR's approach of using #[cfg(feature = "store-sqlite")] and similar gates aligns with standard Rust conventions.

Testing Strategy for Feature Combinations

Because features require an exponential number of configurations and test cases to be 100% covered, by default tests and documentation will only run with the default set of features. The PR's CI workflow addition with 8 feature combinations (visible in .github/workflows/ci.yml) appropriately addresses this challenge. Documentation recommends considering a testing strategy based on time, resources, and cost-benefit of covering specific scenarios in relation to feature combinations.

Feature Testing Tools

For projects utilizing Rust feature flags, it's common to set up a test matrix in CI to individually test all feature flags, though this setup can be difficult to maintain and easy to forget to update. The PR's manual feature matrix in CI (8 configurations) represents a pragmatic approach for a workspace with 3 main features (solver-vrp, solver-ortools, store-sqlite).

Key Design Pattern in PR

A recommended approach is to have a public interface with documentation calling an internal method implemented in conditionally compiled modules. The PR implements this pattern with:

  • Public re-exports in lib.rs (wildside-engine facade)
  • Feature-gated internal modules (e.g., #[cfg(feature = "store-sqlite")] on wildside-core/src/store/sqlite.rs)
  • Fallback error types (MissingFeature) for disabled features

Workspace Feature Coordination

In workspaces with multiple packages, package features can depend on features from other crates, and the plugin can check required features and suggest quick-fixes, though features that depend on each other need manual management. The PR's use of workspace-level feature dependencies (default = ["serde", "store-sqlite"] in root Cargo.toml) coordinates these across multiple crates.

[::web_search::]

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (2)
wildside-cli/src/lib.rs (2)

93-126: LGTM – ingestion pipeline is well-structured.

The execute_ingest function clearly orchestrates the ingestion workflow with proper error wrapping. Each step provides contextual information (path, source error) that will aid debugging. The feature gating ensures this code is only compiled when store-sqlite is enabled.


287-291: LGTM – type alias pattern preserves function signature across features.

The conditional IngestOutcome type (struct vs. ()) is an elegant solution to maintain run_ingest's signature across feature permutations. When store-sqlite is disabled, the function returns early with MissingFeature, so the outcome type is never constructed. The doc comment clearly explains this intent.

Comment thread wildside-cli/src/lib.rs
…erences

- Added missing dependency ordering in GitHub Actions CI workflow.
- Enhanced Markdown formatting in multiple design documents.
- Converted inline numeric citations to Markdown footnotes for clarity.
- Updated tables with footnote references and improved text formatting.
- Added extensive documentation references across several wildside-engine-design docs.
- Minor typo fixes and consistency improvements in code comments and docs.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Fix all issues with AI Agents 🤖
In @.github/workflows/ci.yml:
- Around line 105-108: Add an explicit echo of the TEST_FLAGS before running
tests so CI logs show active feature flags: modify the test step run command
(which currently just executes "make test") to a multi-line shell script that
first runs echo "Testing with flags: $TEST_FLAGS" and then runs "make test",
keeping the existing env mapping for TEST_FLAGS unchanged; ensure the quotes and
variable expansion are correct so the value prints in the workflow logs.
- Around line 69-91: The feature-matrix job is missing a job-level timeout; add
a timeout-minutes entry (e.g., timeout-minutes: 30) to the feature-matrix job
definition so the job fails fast on hangs—insert the line with two-space
indentation under the feature-matrix job (i.e., directly before the existing
strategy: block) so the job 'feature-matrix' has a timeout configured.

In @docs/wildside-engine-design/02-data-foundation.md:
- Around line 278-293: Update the access dates in the footnote citations for
markers [^2], [^3], [^4], [^5], [^6], [^8], [^9], [^10], and [^12] by replacing
"August 13, 2025" with a current, consistent access date (e.g., "January 5,
2026" or another chosen date), ensuring all cited lines in the block use the
same updated date for consistency.

In @docs/wildside-engine-design/05-solver-implementation.md:
- Around line 152-158: Update the three footnote access dates referencing
"August 13, 2025" to "August 13, 2026" in the document; specifically edit the
footnote entries for [^13], [^15], and [^16] in 05-solver-implementation.md so
each occurrence of "August 13, 2025" becomes "August 13, 2026".
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb857b8 and 86b208f.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/02-data-foundation.md
  • docs/wildside-engine-design/05-solver-implementation.md
🧰 Additional context used
📓 Path-based instructions (4)
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
When new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve, proactively update the relevant file(s) in the docs/ directory to reflect the latest state. Ensure the documentation remains accurate and current.
Documentation must use en-GB-oxendict spelling and grammar ("-ize" / "-yse" / "-our") with the exception of the LICENSE filename.

docs/**/*.md: Use British English based on the Oxford English Dictionary locale en-GB, including -ize suffixes (e.g., realize, organization), -lyse suffixes (e.g., analyse, paralyse), -our suffixes (e.g., colour, behaviour), -re suffixes (e.g., centre, calibre), double 'l' (e.g., cancelled, counsellor), maintained 'e' (e.g., likeable, rateable), -ogue suffixes (e.g., analogue, catalogue)
Use the Oxford comma in documentation: 'ships, planes, and hovercraft' where it aids comprehension
Treat company names as collective nouns, for example: 'Concordat Industries are expanding'
Write headings in sentence case
Use Markdown headings (#, ##, ###, and so on) in order without skipping levels
Follow markdownlint recommendations
Provide code blocks and lists using standard Markdown syntax
Always provide a language identifier for fenced code blocks; use 'plaintext' for non-code text
Use '-' as the first level bullet and renumber lists when items change
Prefer inline links using 'text' or angle brackets around the URL
Ensure blank lines before and after bulleted lists and fenced blocks
Ensure tables have a delimiter line below the header row
Expand any uncommon acronym on first use, for example, Continuous Integration (CI)
Wrap paragraphs at 80 columns
Do not wrap tables
Use GitHub-flavoured numeric footnotes referenced as '[^1]'
Footnotes must be numbered in order of appearance in the document
Caption ev...

Files:

  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/02-data-foundation.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Markdown tables and headings must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/02-data-foundation.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/02-data-foundation.md
docs/**/!(README).md

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Avoid first and second person personal pronouns outside the README.md file

Files:

  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/02-data-foundation.md
docs/**/*.{md,rs}

📄 CodeRabbit inference engine (docs/documentation-style-guide.md)

Wrap code at 120 columns

Files:

  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/02-data-foundation.md
🧠 Learnings (2)
📚 Learning: 2026-01-03T14:22:19.441Z
Learnt from: leynos
Repo: leynos/wildside-engine PR: 81
File: docs/wildside-engine-design/02-data-foundation.md:0-0
Timestamp: 2026-01-03T14:22:19.441Z
Learning: Apply en-GB Oxford English spelling in Markdown files by preferring -ize (e.g., normalize, initialize, organize) over -ise, -yse over -yze (e.g., analyse, paralyse), and -our over -or (e.g., colour, favour). The Oxford British English convention favors -ize in these contexts. This guidance should be applied across Markdown documentation in the docs directory to maintain consistent spelling.

Applied to files:

  • docs/wildside-engine-design/01-introduction.md
  • docs/wildside-engine-design/05-solver-implementation.md
  • docs/wildside-engine-design/02-data-foundation.md
📚 Learning: 2026-01-04T04:50:10.691Z
Learnt from: leynos
Repo: leynos/rstest-bdd PR: 384
File: crates/rstest-bdd/tests/async_step_infrastructure.rs:4-4
Timestamp: 2026-01-04T04:50:10.691Z
Learning: In en-GB-oxendict (Oxford English) spelling convention, verbs use the -ize ending (e.g., "normalizes," "organizes," "realizes"), not -ise. The convention is specifically -ize / -yse / -our, so "normalizes" is correct in rstest-bdd documentation and comments.

Applied to files:

  • docs/wildside-engine-design/02-data-foundation.md
🪛 LanguageTool
docs/wildside-engine-design/01-introduction.md

[style] ~16-~16: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...ithms, and solving NP-hard optimization problems all align with the Rust ecosystem's str...

(EN_REPEATEDWORDS_PROBLEM)

docs/wildside-engine-design/05-solver-implementation.md

[inconsistency] ~152-~152: A new year has begun. Did you mean “August 13, 2026”?
Context: ... vrp-core crate on docs.rs, accessed on August 13, 2025, https://docs.rs/vrp-core [^15]: So...

(DATE_NEW_YEAR)


[inconsistency] ~155-~155: A new year has begun. Did you mean “August 13, 2026”?
Context: ...routing problem in Java", accessed on August 13, 2025, <https://softwaremill.com/solving-ve...

(DATE_NEW_YEAR)


[inconsistency] ~157-~157: A new year has begun. Did you mean “August 13, 2026”?
Context: ...P-SAT solver documentation, accessed on August 13, 2025, <https://developers.google.com/optim...

(DATE_NEW_YEAR)

docs/wildside-engine-design/02-data-foundation.md

[typographical] ~1-~1: To join two clauses or introduce examples, consider using an em dash.
Context: # Section 1: The Data Foundation - Ingesting and Integrating Open Data The...

(DASH_RULE)


[style] ~42-~42: This expression can be considered offensive.
Context: ...rate is licensed under the "Do What The Fuck You Want To Public Licence, Version 2" (WTF...

(PROFANITY)


[uncategorized] ~120-~120: The preposition ‘to’ seems more likely in this position.
Context: ...ous reqwest client, issues requests for the Tokio runtime, and always sends a d...

(AI_HYDRA_LEO_REPLACE_FOR_TO)


[style] ~158-~158: You have used the passive voice repeatedly in nearby sentences. To make your writing clearer and easier to read, consider using active voice.
Context: ... referenced by the PoiEntityLinks set are processed further. For those entities, the parser...

(REP_PASSIVE_VOICE)


[style] ~215-~215: Would you like to use the Oxford spelling “initialize”? The spelling ‘initialise’ is also correct.
Context: ...Schema initialization is handled by the initialise_schema function, which enables foreign...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~239-~239: Possible missing preposition found.
Context: ... thousands of queries per user request. Will be rate-limited and result in extreme l...

(AI_HYDRA_LEO_MISSING_IT)


[inconsistency] ~278-~278: A new year has begun. Did you mean “August 13, 2026”?
Context: ... osmpbf crate on crates.io, accessed on August 13, 2025, https://crates.io/crates/osmpbf [^...

(DATE_NEW_YEAR)


[inconsistency] ~280-~280: A new year has begun. Did you mean “August 13, 2026”?
Context: ...freader crate on crates.io, accessed on August 13, 2025, <https://crates.io/crates/osmpbfread...

(DATE_NEW_YEAR)


[inconsistency] ~282-~282: A new year has begun. Did you mean “August 13, 2026”?
Context: ...yword listing on crates.io, accessed on August 13, 2025, https://crates.io/keywords/osm [^5...

(DATE_NEW_YEAR)


[inconsistency] ~284-~284: A new year has begun. Did you mean “August 13, 2026”?
Context: ... wikidata crate on docs.rs, accessed on August 13, 2025, https://docs.rs/wikidata [^6]: spa...

(DATE_NEW_YEAR)


[inconsistency] ~286-~286: A new year has begun. Did you mean “August 13, 2026”?
Context: ...-client crate on crates.io, accessed on August 13, 2025, <https://crates.io/crates/sparql-cli...

(DATE_NEW_YEAR)


[inconsistency] ~288-~288: A new year has begun. Did you mean “August 13, 2026”?
Context: ...al categories on crates.io, accessed on August 13, 2025, <https://crates.io/categories/scienc...

(DATE_NEW_YEAR)


[inconsistency] ~290-~290: A new year has begun. Did you mean “August 13, 2026”?
Context: ...[^9]: geo crate on docs.rs, accessed on August 13, 2025, https://docs.rs/geo [^10]: rstar cra...

(DATE_NEW_YEAR)


[inconsistency] ~291-~291: A new year has begun. Did you mean “August 13, 2026”?
Context: ...0]: rstar crate on docs.rs, accessed on August 13, 2025, https://docs.rs/rstar [^12]: rstar...

(DATE_NEW_YEAR)


[inconsistency] ~293-~293: A new year has begun. Did you mean “August 13, 2026”?
Context: ...2]: rstar crate on docs.rs, accessed on August 13, 2025, https://docs.rs/rstar

(DATE_NEW_YEAR)

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build
🔇 Additional comments (5)
docs/wildside-engine-design/01-introduction.md (1)

1-190: Excellent documentation foundation.

All prior issues have been resolved. The file is well-structured, uses proper Markdown footnote syntax, contains valid Mermaid diagrams, adheres to en-GB-oxendict spelling conventions, and follows the coding guidelines for paragraph wrapping and punctuation. The domain model, spatial indexing design, and runtime architecture are clearly explained with supporting diagrams and sequence flows.

docs/wildside-engine-design/02-data-foundation.md (4)

120-120: Preposition usage is correct; skip flagged suggestion.

LanguageTool suggests changing "for the Tokio runtime" to "to the Tokio runtime," but the current phrasing is semantically and idiomatically correct. The reqwest client issues requests for execution on the Tokio runtime, not to it. No change required.


214-215: Verb spelling is correct per project conventions.

The use of "initialization" (with -ize suffix) complies with the project's en-GB-oxendict standard. The code identifier initialise_schema retains its original spelling. No change required.


239-239: Sentence structure is grammatically correct.

LanguageTool flags a possible missing preposition, but the sentence "Will be rate-limited and result in extreme latency" is complete and idiomatic. Both clauses are properly formed. No change required.


1-277: Document structure and formatting comply with guidelines.

The document follows proper Markdown conventions, maintains consistent British English (en-GB-oxendict) spelling, uses correct heading hierarchy, and properly formats tables and footnotes. The technical content aligns well with the PR objectives regarding ingestion architecture and SQLite-backed data storage. The only outstanding issue is the outdated footnote dates (lines 278–293), which have already been flagged separately.

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml
Comment thread docs/wildside-engine-design/02-data-foundation.md
Comment thread docs/wildside-engine-design/05-solver-implementation.md
- Set 30 minute timeout for feature-matrix job in CI
- Add echo of TEST_FLAGS environment variable before running tests
- Annotate unused variable warning for args in run_ingest when store-sqlite feature disabled

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
wildside-cli/src/lib.rs (1)

56-68: Document the public API function.

The public function run() lacks a doc comment. Document all public APIs using Rustdoc comments (///) for cargo doc generation.

🔎 Proposed fix
+/// Run the Wildside CLI with the current process arguments and environment.
+///
+/// Parses command-line arguments, dispatches to the appropriate subcommand handler,
+/// and returns an error if argument parsing or command execution fails.
+///
+/// # Errors
+///
+/// Returns [`CliError`] if argument parsing fails or if the selected subcommand
+/// encounters an error during execution.
 pub fn run() -> Result<(), CliError> {
♻️ Duplicate comments (1)
wildside-cli/src/lib.rs (1)

73-77: Complete the past review suggestion for parameter-level lint suppression.

The past review comment suggested using #[cfg_attr] on the parameter itself rather than a let binding. The current code has improved by adding a proper #[expect] with reason, but the let binding can still be eliminated by applying the attribute directly to the parameter.

🔎 Complete the suggested refactor

Apply the attribute to the parameter and remove the let binding:

-fn run_ingest(args: IngestArgs) -> Result<IngestOutcome, CliError> {
+fn run_ingest(
+    #[cfg_attr(not(feature = "store-sqlite"), expect(unused_variables, reason = "args not used when store-sqlite disabled"))]
+    args: IngestArgs
+) -> Result<IngestOutcome, CliError> {
     #[cfg(not(feature = "store-sqlite"))]
     {
-        #[expect(
-            unused_variables,
-            reason = "args only used when store-sqlite is enabled"
-        )]
-        let _args = args;
         return Err(CliError::MissingFeature {
🤖 Prompt for AI Agents
In @wildside-cli/src/lib.rs at line 70, update the run_ingest function signature to apply #[cfg_attr(not(feature = "store-sqlite"), expect(unused_variables, reason = "args not used when store-sqlite disabled"))] to the args parameter, and remove lines 73-77 (the #[expect] attribute and let _args = args; statement). The updated signature should span lines 70-73, with the opening brace on line 74.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86b208f and a4fe602.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • wildside-cli/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Run make check-fmt, make lint, and make test before committing in Rust projects.
make check-fmt executes cargo fmt --workspace -- --check to validate formatting across the entire workspace without modifying files.
make lint executes cargo clippy --workspace --all-targets --all-features -- -D warnings to lint every target with all features enabled and deny all Clippy warnings.
make test executes cargo test --workspace to run the full workspace test suite.
Clippy warnings MUST be disallowed in Rust code.
Fix any warnings emitted during tests in the Rust code itself rather than silencing them.
Where a Rust function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a Rust function has too many parameters, group related parameters in meaningfully named structs.
Where a Rust function is returning a large error, consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality in Rust. Run both before and after making any change.
Every Rust module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs in Rust using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings in Rust.
Handle errors with the Result type instead of panicking in Rust where feasible.
Avoid unsafe code in Rust unless absolutely necessary, and document any usage clearly.
Place function attributes after doc comments in Rust.
Do not use return in single-line Rust functions.
Use predicate functions for conditional criteria in Rust with more than two branches.
Rust lints must not be silenced except as a last resort.
Rust lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow in Rust.
Use rstest fixtures for shared setup in Rust tests.
Replace duplicated Rust tests with `#[rstest(...)...

Files:

  • wildside-cli/src/lib.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • wildside-cli/src/lib.rs
🧬 Code graph analysis (1)
wildside-cli/src/lib.rs (5)
wildside-core/src/store/spatial_index.rs (1)
  • write_spatial_index (90-95)
wildside-data/src/wikidata/etl/mod.rs (1)
  • extract_linked_entity_claims (153-198)
wildside-data/src/wikidata/store/persistence.rs (1)
  • persist_claims_to_path (285-295)
wildside-data/src/ingest/mod.rs (1)
  • ingest_osm_pbf_report (149-202)
wildside-data/src/ingest/sqlite.rs (1)
  • persist_pois_to_sqlite (97-122)
🔍 Remote MCP

I'll search for relevant information to help you review this PR effectively. Given the focus on feature flags, workspace dependencies, and conditional compilation, let me gather some context.

Summary of Relevant Context for PR Review

Based on comprehensive research into Rust feature flag patterns, workspace management, and conditional compilation best practices, here's the relevant context for effectively reviewing this Phase 4 PR:

Feature Flags & Optional Dependencies

Every package must ensure that default-features = false is specified to avoid enabling default features automatically, since dependencies enable default features unless default-features = false is specified. Optional dependencies can be marked "optional" to exclude them from compilation by default, and implicitly define a feature with the same name, which can be referenced with cfg(feature = "...") syntax.

Key insight for review: This PR's approach of marking wildside-solver-ortools and wildside-solver-vrp as optional dependencies and gating them behind feature flags follows best practices.

Feature Unification in Workspaces

When building multiple packages in a workspace, the features of the dependencies of all packages are unified, and if you want to avoid that unification for different workspace members, you need to build them via separate cargo invocations. If a crate has N independent features, then all 2^N possible build combinations can occur in practice, and it's important to ensure CI covers all these combinations.

Key insight for review: The PR's feature-matrix CI job testing 8 distinct feature flag combinations is an excellent defensive measure against feature unification issues.

Workspace Dependencies Best Practices

Workspace dependencies can inherit base definitions from [workspace.dependencies], and features declared inline are additive with features from the workspace definition. Optional dependencies can be combined with the workspace = true key, and features specified inline are unioned with features in the workspace table.

Key insight for review: The PR's use of workspace.dependencies to centralize dependency management while allowing member crates to selectively mark dependencies as optional is architecturally sound.

Conditional Compilation Patterns

The goal should be to have readable code without #[cfg] everywhere in confusing combinations, and maintain scalable conditional compilation in a maintainable manner. Off-by-default code should be trivially correct (e.g., dummy shims that do nothing or return errors), and if off-by-default code is nontrivial, it should have dedicated CI jobs to ensure conditional parts are tested.

Key insight for review: The PR's approach of returning MissingFeature errors for disabled runtime paths, with dedicated test coverage in wildside-cli/src/tests/feature_flag_steps.rs and feature_flags.rs, is a good implementation of this pattern.

Re-export Facade Pattern

The PR implements a root-crate re-export facade (src/lib.rs) that conditionally exports solver/store types without changing public APIs in breaking ways, since enabling a feature should not introduce SemVer-incompatible changes.

Key insight for review: The approach of re-exporting core types unconditionally while gating implementation types (SqlitePoiStore, VrpSolver, OrtoolsSolver) maintains API stability while allowing pluggable implementations.

Areas to Focus Review

  1. Feature flag consistency: Verify all feature-gated conditional compilation uses the same feature names across Cargo.toml and source code
  2. Default features impact: The change from default = ["serde"] to default = ["serde", "store-sqlite"] in wildside-core is a significant default set change—confirm this doesn't break downstream consumers
  3. Error handling for missing features: Validate that MissingFeature errors provide clear guidance on which features must be enabled for given operations
  4. Test coverage breadth: The feature-matrix CI job is good; ensure it covers critical user workflows under each combination
  5. Documentation of feature-gating: The design doc updates should clearly explain feature flag purposes and trade-offs

[::web_search::]

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (5)
.github/workflows/ci.yml (4)

69-72: Excellent job setup with appropriate safeguards.

The needs: build dependency and timeout-minutes: 30 constraint provide good CI hygiene—feature tests won't run if basic validation fails, and hung tests will fail fast rather than consuming resources indefinitely. The structure is sound.


73-92: Comprehensive feature matrix coverage.

The eight configurations provide excellent coverage of feature combinations: all three features in isolation, key pairwise combinations, no-features baseline, and all-features maximum. The fail-fast: false strategy ensures you'll see failures across all combinations rather than stopping at the first, which is valuable for diagnosing feature-specific issues. This aligns with best practices for testing workspace feature unification.


106-111: Test execution with good observability.

The explicit echo of TEST_FLAGS at line 108 provides valuable debugging context in CI logs when investigating feature-specific failures. The environment variable plumbing through to the Makefile is correct.


69-111: Verify that all feature-matrix tests pass.

The PR summary notes "Remaining work noted: formatting, linting, and full test validation across feature permutations." Confirm that all eight feature combinations in this matrix now pass successfully before merging, as the matrix is only valuable if the underlying code handles all these configurations correctly.

Run this workflow manually or verify in CI that all matrix entries complete successfully:

#!/bin/bash
# Description: Check the status of feature-matrix tests in recent CI runs

# Query recent workflow runs for this PR
gh run list --workflow=ci.yml --limit 5 --json conclusion,status,displayTitle,url

# For detailed logs of the most recent run:
# gh run view --log
wildside-cli/src/lib.rs (1)

90-295: Feature-gated design is well implemented.

The feature-gated ingestion implementation demonstrates several strengths:

  • Consistent feature gating: All store-sqlite-dependent code (imports, functions, types) is properly conditionally compiled.
  • Dual-type IngestOutcome approach: The use of a struct definition when store-sqlite is enabled and a unit type fallback when disabled (lines 291-295) cleverly preserves the function signature across feature permutations while preventing dead code compilation. The doc comment clearly explains the rationale.
  • Improved error handling: The require_existing function (lines 237-256) appropriately distinguishes MissingSourceFile (file not found or not a file) from SourceFileAccess (IO errors), providing clear diagnostics with field names.
  • Clean orchestration: The execute_ingest workflow (lines 97-130) cleanly sequences OSM PBF ingestion, Wikidata claims extraction, and spatial index generation with proper error context.

This aligns well with the PR objectives for Phase 4 feature-flagging.

…isabled

Replace unused variable annotation with drop() for args in non-store-sqlite builds.
This removes unnecessary attributes and clarifies intent when the feature is not enabled.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@leynos leynos changed the title Phase 4: Feature flags, root engine facade, and doc decomposition Phase 4: Feature flags, root engine facade, and modular docs Jan 5, 2026
@leynos leynos merged commit 894aa38 into main Jan 5, 2026
12 checks passed
@leynos leynos deleted the terragon/feature-flags-implementation-phase-4-dk5292 branch January 5, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant