Skip to content

Adopt the Whitaker Dylint suite in the lint gate and CI - #269

Merged
leynos merged 8 commits into
mainfrom
adopt-whitaker
Jul 19, 2026
Merged

Adopt the Whitaker Dylint suite in the lint gate and CI#269
leynos merged 8 commits into
mainfrom
adopt-whitaker

Conversation

@leynos

@leynos leynos commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

This pull request adopts the Whitaker Dylint suite in lille's lint gate and continuous integration, as part of the estate-wide rollout that began with leynos/netsuke#410. The suite runs after Clippy in make lint, and CI installs it with a pinned whitaker-installer (0.2.5) using cargo binstall where available and a locked cargo install fallback otherwise.

Running the suite surfaced findings across four lints, all of which are fixed here rather than suppressed:

  • module_max_lines: the map, dbsp_circuit::streams::behaviour, dbsp_sync::output, and test_utils::physics modules exceeded the 400-line budget and are now split into focused sibling submodules, following the directory layout already used by streams::floor and streams::health.
  • module_must_have_inner_docs: __macro_deps, thirteen in-source test modules, and the dbsp_test_support helper module gained //! inner doc comments; the shared test-support files now place their doc comment before the #![cfg(feature = "test-support")] attribute.
  • no_expect_outside_tests: test fixtures (new_circuit in both the test_utils crate and the in-crate stream helper, build_circuit, run_health_delta, fresh_state, make_state, and setup_app) now return Result and propagate construction errors; unwrapping happens only in test bodies, where a failure is the test verdict. The shared assert_health_delta helper became a macro so panic locations point at the calling test.
  • no_unwrap_or_else_panic: the rspec runner constructs its Configuration directly from public fields instead of using the fallible builder with a panicking fallback.

The existing dylint.toml exclusion of the build_support build-script crate from no_std_fs_operations remains sufficient; no new exclusions were required.

Review walkthrough

Validation

  • env -u WHITAKER RUSTFLAGS="-D warnings" whitaker --all -- --all-targets --all-features exits 0.
  • env -u WHITAKER make check-fmt lint typecheck test markdownlint nixie passes in full: formatting clean, cargo doc and Clippy warning-free, the Whitaker suite clean, all unit, integration, and behavioural tests green, Markdown lint clean, and Mermaid validation reporting no errors.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The pull request adds DBSP behaviour and ECS output pipelines, extracts primary map lifecycle systems, makes test circuit construction fallible, introduces reusable physics test types, and expands Whitaker linting, CodeScene configuration, and test documentation.

Changes

Behaviour streams

Layer / File(s) Summary
Behaviour stream pipeline
src/dbsp_circuit/streams/behaviour/*
Adds fear defaults, movement decision calculation and deduplication, movement application, module exports, and associated API documentation.
Behaviour stream validation
src/dbsp_circuit/streams/behaviour/tests.rs
Tests fear thresholds, missing targets, zero displacement, conflicting targets, duplicate targets, and normalized movement decisions.

DBSP output bridge

Layer / File(s) Summary
Output application pipeline
src/dbsp_sync/output/mod.rs
Applies DBSP positions, velocities, and health deltas to ECS state, handles invalid values and duplicates, drains outputs, and reports step failures.
Output bridge validation
src/dbsp_sync/output/tests/*
Tests successful updates, duplicate suppression, conversion bounds, health guards, clamping, unchanged invalid outputs, and error events.

Map lifecycle extraction

Layer / File(s) Summary
Primary map lifecycle
src/map/lifecycle.rs
Implements primary map spawning, path validation, unloading, asset tracking, load monitoring, and lifecycle logging.
Plugin wiring and lifecycle tests
src/map/mod.rs, src/map/lifecycle.rs
Registers lifecycle systems and observers and tests spawn and validation conditions.

Fallible DBSP test infrastructure

Layer / File(s) Summary
Fallible circuit helpers
src/dbsp_circuit/streams/test_utils.rs, src/dbsp_circuit/streams/{floor,health,kinematics}/tests/*, src/dbsp_circuit/tests.rs, src/dbsp_sync/{duplicate_filter,input}/mod.rs
Propagates circuit construction errors through test helpers and updates callers to handle Result values.
Physics test types and assertions
test_utils/src/physics/*, src/dbsp_circuit/tests.rs
Moves physics records into newtype wrappers, adds constructors and conversions, and centralises health-delta assertions in a macro.

Test utility package

Layer / File(s) Summary
Test utility contracts
test_utils/Cargo.toml, test_utils/src/lib.rs
Adds DBSP and rstest dependencies, updates fallible examples, and tests shared assertion helpers.

Quality tooling and documentation

Layer / File(s) Summary
Whitaker lint integration
.github/workflows/ci.yml, Makefile
Adds versioned Whitaker installation, caching, and lint execution across targets and features.
Code health and support cleanup
.codescene/*, src/*, tests/support/*
Scopes a CodeScene rule exception, documents test modules, relocates feature gates, and simplifies RSpec configuration construction.

Sequence Diagram(s)

sequenceDiagram
  participant Inputs
  participant movement_decision_stream
  participant apply_movement
  participant ECS
  Inputs->>movement_decision_stream: provide fear, targets, and positions
  movement_decision_stream->>apply_movement: emit deduplicated movement decisions
  apply_movement->>ECS: emit updated positions
Loading

Possibly related PRs

Poem

Maps wake, streams decide,
Fear turns vectors toward or wide,
ECS catches each new trace,
Whitaker guards the build-place,
Tests make failures clear.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (3 errors, 7 warnings)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error FAIL: the invalid-path test can still pass when try_spawn_primary_map_on_build returns early for a missing AssetServer, and apply_movement has no direct tests. Assert InvalidPrimaryMapAssetPath via an observer, and add end-to-end tests that feed base positions plus movement decisions (including duplicates) and verify emitted positions.
Unit Architecture ❌ Error apply_dbsp_outputs_system returns early on step failure, leaving circuit inputs uncleared; output application also ignores Z-set weights, so mutation boundaries stay hidden. Clear inputs on every exit path, branch on record weights before mutating ECS, and add tests for the failure and negative-weight paths.
Rust Compiler Lint Integrity ❌ Error behaviour/tests.rs still suppresses unused_mut, and fear.rs/map/lifecycle.rs keep avoidable clones and a throwaway StreamConcat wrapper. Remove #[expect(unused_mut)], call fears.plus(&missing) directly, and borrow tracking.handle.as_ref() instead of cloning it.
Title check ⚠️ Warning The title matches the main change, but it omits the required rollout or issue reference. Add the relevant roadmap or issue reference to the title, such as the linked rollout item or issue number.
Developer Documentation ⚠️ Warning No docs/developers-guide.md exists, and the PR adds Whitaker/tooling plus new API boundaries without matching docs/ADR/execplan updates. Add docs/developers-guide.md, update the relevant design/ADR pages for Whitaker, module splits, and fallible helpers, and mark any completed roadmap items/execplans done.
Testing (Property / Proof) ⚠️ Warning Invariant-heavy code in behaviour/output/map adds only example tests; no proptest/Kani/Verus coverage or proof exists for the new range-based invariants. Add property tests (or a proof) for movement dedupe/normalisation, health-delta retraction/duplicate handling, and asset-path validation; keep the current unit tests as examples.
Domain Architecture ⚠️ Warning apply_dbsp_outputs_system consolidates DBSP outputs but ignores Z-set weights, so retractions/duplicates can mutate ECS state instead of being enforced at the boundary. Handle OrdZSet weights before mutating ECS, and make duplicate-movement enforcement explicit inside apply_movement or keep the helper private.
Performance And Resource Use ⚠️ Warning fear.rs clones fears; lifecycle.rs clones tracking.handle every tick; apply.rs builds a fresh HashMap per batch. Use borrowed handles/streams, remove redundant clones, and if duplicate checks stay, reuse a bounded scratch map or keep them debug-only.
Concurrency And State ⚠️ Warning apply_dbsp_outputs_system returns early on step errors before clear_inputs(), so stale inputs can replay on the next tick; tests only cover the error event. Move clear_inputs() into a failure-safe path and add a retry test that fails once, then succeeds without replaying stale inputs.
Architectural Complexity And Maintainability ⚠️ Warning Fail: it adds one-off abstraction layers with no reuse story—StreamConcat only renames .plus(), apply_output_records! serves two call sites, and public apply_movement trusts upstream dedupe. Inline the trait and macro, and enforce dedupe inside apply_movement or make it internal; keep only module splits and helpers that clearly reduce complexity.
✅ Passed checks (10 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly matches the lint-gate and CI Whitaker adoption work and the related refactors.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
User-Facing Documentation ✅ Passed PASS: treat the PR as internal refactoring/CI/test work; no new user-facing feature landed, and the existing map design docs already cover the relevant behaviour.
Module-Level Documentation ✅ Passed All changed module files begin with //! docs, and each new submodule explains its role and relation to neighbouring systems.
Testing (Unit And Behavioural) ✅ Passed PASS: Added unit and integration-style tests cover edge cases, error paths, and system boundaries (DBSP/Bevy), with no disguised helper-only suite.
Testing (Compile-Time / Ui) ✅ Passed No new compile-time or UI surface is introduced here; the added macro and output paths are covered by ordinary unit/integration tests, not snapshot or trybuild tests.
Observability ✅ Passed Approve: cover map/load/unload boundaries and DBSP output anomalies with logs and error events carrying path/entity context.
Security And Privacy ✅ Passed No secrets, auth gaps, or unsafe sinks were introduced; CI pins Whitaker, map paths reject absolute/traversal inputs, and logs only intentional asset/error details.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch adopt-whitaker

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

codescene-access[bot]

This comment was marked as outdated.

Comment thread src/dbsp_sync/duplicate_filter.rs Outdated
Comment thread test_utils/src/physics/units.rs
codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos enabled auto-merge (squash) July 14, 2026 21:28
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

leynos added 6 commits July 19, 2026 14:14
Prepare the codebase for adopting the Whitaker Dylint suite by fixing
every finding it reports:

- Split `map`, `dbsp_circuit::streams::behaviour`, `dbsp_sync::output`,
  and `test_utils::physics` into sibling submodules so no module
  exceeds the 400-line budget (`module_max_lines`).
- Add `//!` inner doc comments to `__macro_deps`, thirteen in-source
  test modules, and the `dbsp_test_support` helper module, and move
  the `#![cfg(feature = "test-support")]` attribute below the existing
  doc comment in the shared test-support files
  (`module_must_have_inner_docs`).
- Make test fixtures fallible instead of panicking: `new_circuit`
  (both the `test_utils` crate and the in-crate stream helper),
  `build_circuit`, `run_health_delta`, `fresh_state`, `make_state`,
  and `setup_app` now return `Result` and propagate errors; the test
  bodies unwrap, so a failure is the test verdict
  (`no_expect_outside_tests`).
- Replace the tick counter's panicking `checked_add().expect()` with
  `saturating_add` inside the generator closure.
- Convert `assert_health_delta` into a macro so panic locations point
  at the calling test and the fallible circuit run executes inside the
  test body.
- Build the rspec `Configuration` directly from its public fields
  instead of the fallible builder, removing the panicking
  `unwrap_or_else` fallback (`no_unwrap_or_else_panic`).
- Add a direct `dbsp` dependency to `test_utils` so its fixtures can
  name `dbsp::Error` in their return types.
Wire the Whitaker Dylint suite into the standard lint path as part of
the estate-wide rollout (see leynos/netsuke#410):

- Makefile: add a `WHITAKER ?= whitaker` tool variable and run the
  suite after Clippy in the `lint` target with `-D warnings` across
  all targets and features.
- CI: install the suite via a pinned `whitaker-installer` (0.2.5)
  before the lint step, preferring `cargo binstall` and falling back
  to `cargo install --locked` when binstall is unavailable, with the
  installer binary and binstall cache keyed by OS, architecture, and
  installer version. The lint step now runs the suite after Clippy.

The repository's existing `dylint.toml` already excludes the
`build_support` build-script crate from `no_std_fs_operations`; no
further exclusions were needed.
Deduplicate the record-twice duplicate-counting tests in
`dbsp_sync::duplicate_filter` behind an `assert_duplicate_counted!`
macro shared by the sequenced and unsequenced ingestion paths. A macro
rather than a helper function keeps panic locations pointing at the
calling test.

Add a narrowly scoped `.codescene/code-health-rules.json` disabling
Primitive Obsession for `test_utils/src/physics/units.rs` only: the
module consists of newtype wrapper constructors, which necessarily
accept primitives; it exists to remove primitive obsession from call
sites, so the metric is a categorical false positive there.
The initial exact-file `matching_content_path` coincided with a
CodeScene delta-analysis error. Use the documented directory-glob
form scoped to the `test_utils` crate and drop the optional `usage`
field, matching CodeScene's published examples.
Installer 0.2.5 provisions cargo-dylint 4.1.0, whose dylint driver
cannot build on the suite's new nightly-2026-05-28 pin; 0.2.6
provisions cargo-dylint 6.0.1.
The Whitaker adoption branch's lint-driven refactors (helper extractions
and module splits) landed without tests for the extracted paths, dropping
line coverage to 84.3% against the CodeScene ratchet baseline of 85.4%.

Cover the undercovered code introduced or moved by this branch:

- Exercise the DBSP output guard paths: f32-from-f64 conversion bounds,
  out-of-range position and velocity records, and health deltas for
  unmappable, unknown, despawned, or component-less entities, plus
  clamping overkill damage to zero. Split the output tests into a
  directory module to stay within the file size budget.
- Exercise the movement-decision accumulator: zero-displacement
  decisions, conflicting targets normalising to one averaged decision,
  and opposing targets cancelling to a zero vector.
- Cover the map lifecycle helpers that need no asset backend: asset-path
  validation rejects empty, absolute, and parent-traversal paths, and
  the build-time spawn skips when disabled, already spawned, invalid,
  or missing an asset server.
- Cover the sequenced duplicate filter's unsequenced early return.
- Cover the test_utils constructors and assertion helpers that were
  previously only exercised by doctests, which the coverage run does
  not instrument.

Line coverage rises from 84.67% to 89.65% under the CI feature set.
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

src/dbsp_sync/duplicate_filter.rs

Comment on file

    #[rstest]
    fn sequenced_event_reapplied_in_next_frame_is_ignored() {
        let mut state = fresh_state();
        let mut state = fresh_state().expect("failed to initialise DbspState");

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: tests.sequenced_event_reapplied_in_next_frame_is_ignored,tests.unsequenced_event_is_not_a_sequenced_duplicate

@coderabbitai

This comment was marked as resolved.

CodeScene flagged duplication between
unsequenced_event_is_not_a_sequenced_duplicate and
sequenced_event_reapplied_in_next_frame_is_ignored: both built a fresh
DbspState and an empty seen set the same way before exercising their
distinct branches of record_duplicate_sequenced_damage. Factor that
construction into fresh_sequenced_filter, placed alongside fresh_state,
and destructure its result in each test while keeping the expect calls
local so failures still attribute to the calling test.
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

src/dbsp_sync/duplicate_filter.rs

Comment on lines +156 to +157

        let (mut state, mut seen) =
            fresh_sequenced_filter().expect("failed to initialise DbspState");

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: tests.sequenced_event_reapplied_in_next_frame_is_ignored,tests.unsequenced_event_is_not_a_sequenced_duplicate

Replace exact float equality assertions in the DBSP output edge-case
tests with epsilon-bounded checks, matching the tolerance pattern used
elsewhere in the test suite. Move monitor_primary_map_load_state ahead
of the lifecycle test module so no items trail the test module.
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current PR revision =='
git rev-parse --short HEAD
git status --short

printf '%s\n' '== File outline =='
ast-grep outline src/dbsp_sync/duplicate_filter.rs --items all

printf '%s\n' '== Relevant tests and fixtures =='
rg -n -A180 -B20 \
  'fn fresh_state\b|fn fresh_sequenced_filter\b|fn unsequenced_event_is_not_a_sequenced_duplicate\b|fn sequenced_event_reapplied_in_next_frame_is_ignored\b|fn sequenced_event\b|fn unsequenced_event\b' \
  src/dbsp_sync/duplicate_filter.rs

printf '%s\n' '== Production duplicate-filter branches =='
rg -n -A100 -B15 \
  'fn record_duplicate_sequenced_damage\b|fn record_duplicate_unsequenced_damage\b' \
  src/dbsp_sync/duplicate_filter.rs

Length of output: 11652


@leynos src/dbsp_sync/duplicate_filter.rstests.unsequenced_event_is_not_a_sequenced_duplicate, tests.sequenced_event_reapplied_in_next_frame_is_ignored

Suppress this CodeScene finding. Validate against current PR head 9c8e68a: the shared fixture construction already resides in tests.fresh_sequenced_filter, so no meaningful setup duplication remains.

Paste the following into the CodeScene diagnostic suppression field:

These tests deliberately retain a similar Arrange–Act–Assert structure while covering separate branches of DbspState::record_duplicate_sequenced_damage. unsequenced_event_is_not_a_sequenced_duplicate verifies the seq: None early-return contract: an unsequenced event must never be treated as a sequenced duplicate, even when applied repeatedly. sequenced_event_reapplied_in_next_frame_is_ignored verifies the independent persisted-state contract: an event matching DbspState::applied_health must be rejected and counted as a duplicate.

The shared fixture construction has already been extracted into fresh_sequenced_filter. Further parameterization or merging would obscure the branch-specific preconditions and expected outcomes, make failures less diagnostic, and couple two distinct behavioural contracts solely to satisfy structural-similarity detection.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Gates Failed
Enforce advisory code health rules (1 file with Code Duplication)

Our agent can fix these. Install it.

Gates Passed
5 Quality Gates Passed

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
duplicate_filter.rs 1 advisory rule 10.00 → 9.39 Suppress

See analysis details in CodeScene

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No quality gates enabled for this code.

@leynos
leynos merged commit 736fb2b into main Jul 19, 2026
8 of 10 checks passed
@leynos
leynos deleted the adopt-whitaker branch July 19, 2026 21:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 17

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

Inline comments:
In @.github/workflows/ci.yml:
- Line 15: Update the Whitaker setup logic that uses WHITAKER_INSTALLER_VERSION
so an existing whitaker-installer is reused only when its --version matches the
pinned version; otherwise perform installation or setup for the requested
version. Do not rely on command -v alone, and preserve the existing behavior for
a matching installation.

In `@src/dbsp_circuit/streams/behaviour/apply.rs`:
- Around line 75-105: Update apply_movement’s movement stream before the
base_idx.join call to fold duplicate decisions by entity into one movement
decision, mirroring dedupe_movement_decisions in decide.rs. Use the deduplicated
stream for both duplicate validation and joining, while preserving the existing
warning/debug assertion behavior.

In `@src/dbsp_circuit/streams/behaviour/decide.rs`:
- Around line 215-240: Remove the total_weight == 0 reset blocks from
MovementAccumulator::apply and MovementAccumulator::merge, leaving their
additive sum and weight updates intact. Preserve into_decision’s existing
handling of zero total weight.

In `@src/dbsp_circuit/streams/behaviour/fear.rs`:
- Around line 11-19: Remove the speculative StreamConcat trait and its
implementation, then update the affected call site to invoke plus directly on
the fears stream with missing as its argument. Preserve the existing
concatenation behavior.
- Around line 77-88: Remove the redundant clone in the fear stream construction
by binding `explicit` directly to the borrowed `fears` reference. Keep the
existing `map_index` and `concat` calls unchanged, relying on their `&self`
receivers.

In `@src/dbsp_circuit/streams/behaviour/tests.rs`:
- Around line 119-122: Remove the #[expect(unused_mut)] annotation and the
unnecessary mutability from the circuit binding in this test, matching the
non-mut circuit bindings used by the other tests while preserving the existing
.step() call.
- Around line 65-73: Replace the inline RootCircuit construction in the affected
tests with the existing build_decision_circuit() fixture. Apply this to all
three duplicated setup blocks, remove now-unused target input/push handling
where applicable, and preserve each test’s existing assertions and inputs.

In `@src/dbsp_sync/output/mod.rs`:
- Around line 111-114: Remove the cognitive-complexity #[expect] from the
function containing should_apply_health_delta by extracting the entity/Health
resolution guard block into a helper such as resolve_health_target. Have the
helper return Option<Mut<Health>>, preserve all existing validation and
early-return behavior, and use its result in the parent function so the lint
passes without suppression.
- Around line 14-38: Replace the apply_output_records! macro with an explicit
helper function that resolves a record’s entity through state.id_map and
retrieves the mutable Transform, VelocityComp, and Health components from
DbspWriteQuery, returning None when either lookup fails. Update both call sites
to iterate records and apply their record-specific logic directly, preserving
the current skip behavior while avoiding pattern-fragment macro binding.
- Around line 115-190: In apply_health_deltas, inspect each consolidated OrdZSet
weight before calling should_apply_health_delta or mutating Health; apply only
positive weights and skip negative retractions (and zero weights). Ensure
retractions do not update applied_health or world_handle, rather than relying
solely on expected_health_retractions.
- Around line 26-38: Update the apply_output_records! macro to inspect each
record’s Z-set weight before mutating ECS state, and skip records with
non-positive weights (or explicitly handle retractions) so only positive output
records reach the query.get_mut and body operations. Preserve the existing
entity lookup and pattern-matching behavior for applicable records.
- Around line 200-225: Ensure apply_dbsp_outputs_system clears circuit inputs
even when state.step_circuit() returns an error. Move
state.circuit.clear_inputs() into a cleanup path that executes before both
success and early-return error paths, while preserving the existing output
application and error-trigger behavior.

In `@src/dbsp_sync/plugin.rs`:
- Line 95: Update the affected documentation wording in src/dbsp_sync/plugin.rs
at lines 95-95 and src/logging.rs at lines 58-58, replacing
“initialises”/“initialisation” with “initializes”/“initialization” as specified;
no code behavior changes are needed.

In `@src/map/lifecycle.rs`:
- Line 127: Update the asset path validation around the visible contains check
to split the path on `/` and reject only when a component equals `..`, allowing
filenames such as `maps/primary..backup.tmx`; add a test covering this accepted
case.
- Around line 295-301: Update build_spawn_rejects_invalid_path_without_spawning
to register an observer for InvalidPrimaryMapAssetPath before calling
try_spawn_primary_map_on_build, then assert the observer was triggered. Retain
the existing assertion that no PrimaryTiledMap entity is spawned, ensuring the
test verifies both rejection and absence of a map.
- Around line 190-193: Update the #[expect(clippy::too_many_arguments)]
suppressions in src/map/lifecycle.rs at lines 190-193, 231-234, and 312-315:
either add an issue or roadmap reference to each suppression to track the
in-progress work, or remove the suppressions if no tracking reference exists.
- Around line 325-327: Update the handle binding in the tracking lifecycle logic
to borrow via tracking.handle.as_ref() instead of cloning the Option or handle.
Preserve the early return when no handle is present, and continue obtaining the
identifier through handle.id().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c0808875-ccf2-4cdc-8055-445495eedf8f

📥 Commits

Reviewing files that changed from the base of the PR and between e6e8bc2 and 9c8e68a.

📒 Files selected for processing (41)
  • .codescene/code-health-rules.json
  • .github/workflows/ci.yml
  • Makefile
  • src/dbsp_circuit/streams/behaviour.rs
  • src/dbsp_circuit/streams/behaviour/apply.rs
  • src/dbsp_circuit/streams/behaviour/decide.rs
  • src/dbsp_circuit/streams/behaviour/fear.rs
  • src/dbsp_circuit/streams/behaviour/mod.rs
  • src/dbsp_circuit/streams/behaviour/tests.rs
  • src/dbsp_circuit/streams/floor/tests.rs
  • src/dbsp_circuit/streams/health/tests.rs
  • src/dbsp_circuit/streams/kinematics/tests/motion.rs
  • src/dbsp_circuit/streams/kinematics/tests/position_floor.rs
  • src/dbsp_circuit/streams/test_utils.rs
  • src/dbsp_circuit/tests.rs
  • src/dbsp_sync/damage_inbox.rs
  • src/dbsp_sync/duplicate_filter.rs
  • src/dbsp_sync/input/mod.rs
  • src/dbsp_sync/mod.rs
  • src/dbsp_sync/output.rs
  • src/dbsp_sync/output/mod.rs
  • src/dbsp_sync/output/tests/edge_cases.rs
  • src/dbsp_sync/output/tests/mod.rs
  • src/dbsp_sync/plugin.rs
  • src/dbsp_sync/state.rs
  • src/lib.rs
  • src/logging.rs
  • src/map/lifecycle.rs
  • src/map/mod.rs
  • src/map/spawn.rs
  • src/map/translate.rs
  • src/numeric.rs
  • src/physics.rs
  • src/presentation.rs
  • test_utils/Cargo.toml
  • test_utils/src/lib.rs
  • test_utils/src/physics/mod.rs
  • test_utils/src/physics/units.rs
  • tests/support/map_fixture.rs
  • tests/support/map_test_plugins.rs
  • tests/support/rspec_runner.rs
💤 Files with no reviewable changes (2)
  • src/dbsp_sync/output.rs
  • src/dbsp_circuit/streams/behaviour.rs

Comment thread .github/workflows/ci.yml
env:
CARGO_TERM_COLOR: always
BUILD_PROFILE: debug
WHITAKER_INSTALLER_VERSION: '0.2.6'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- .github/workflows/ci.yml (relevant lines) ---'
nl -ba .github/workflows/ci.yml | sed -n '1,120p'

echo
echo '--- Whitaker references ---'
rg -n "WHITAKER_INSTALLER_VERSION|whitaker-installer|Whitaker" .github/workflows . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' || true

Repository: leynos/lille

Length of output: 240


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- .github/workflows/ci.yml (first 140 lines with line numbers) ---'
awk '{ printf "%6d  %s\n", NR, $0 }' .github/workflows/ci.yml | sed -n '1,140p'

echo
echo '--- Whitaker references ---'
rg -n "WHITAKER_INSTALLER_VERSION|whitaker-installer|Whitaker" .github/workflows . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' || true

Repository: leynos/lille

Length of output: 5453


Check the installed Whitaker version before skipping setup. command -v only proves the binary exists, so a preinstalled or stale whitaker-installer can bypass the pinned WHITAKER_INSTALLER_VERSION and run a different suite. Validate whitaker-installer --version before reusing it.

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

In @.github/workflows/ci.yml at line 15, Update the Whitaker setup logic that
uses WHITAKER_INSTALLER_VERSION so an existing whitaker-installer is reused only
when its --version matches the pinned version; otherwise perform installation or
setup for the requested version. Do not rely on command -v alone, and preserve
the existing behavior for a matching installation.

Comment on lines +75 to +105
let mv = mv_base.inspect(|batch| {
// Accumulate counts per entity to catch duplicates emitted within a
// single tick. Duplicates indicate a bug upstream; release builds log
// the issue while debug builds panic for visibility.
use std::collections::HashMap;

let mut counts: HashMap<i64, i64> = HashMap::new();
for (entity, _mv, weight) in batch.iter() {
*counts.entry(entity).or_default() += weight;
}
for (entity, total) in counts {
if total > 1 {
warn!("duplicate movement decisions for entity {entity}");
}
debug_assert!(
total <= 1,
"duplicate movement decisions for entity {entity}"
);
}
});

let moved = base_idx.join(&mv, |_, p, &(dx, dy)| Position {
entity: p.entity,
x: OrderedFloat(p.x.into_inner() + dx.into_inner()),
y: OrderedFloat(p.y.into_inner() + dy.into_inner()),
z: p.z,
});
let mv_entities = mv.map(|(e, _)| *e).map_index(|e| (*e, ()));
let unmoved = base_idx.antijoin(&mv_entities).map(|(_, p)| *p);
unmoved.plus(&moved)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Duplicate movement decisions are only detected, never corrected, in release builds.

debug_assert! (line 89) is compiled out in release, leaving only a warn!. The subsequent base_idx.join(&mv, ...) (line 96) still performs a full join, so if the invariant is ever violated an entity yields multiple Position rows in moved, silently corrupting the output Z-set that later drives ECS state — the invariant is checked, not enforced. apply_movement is public and exported independently of movement_decision_stream's dedupe, so nothing stops a future caller from feeding it un-deduplicated movement data.

Fold duplicate decisions per entity into a single one before joining (mirroring the dedupe_movement_decisions approach in decide.rs), rather than relying on the caller to have already deduplicated.

As per coding guidelines, "Commands must honestly enforce or delegate the invariants they claim to enforce" and code must not "rely on caller discipline or untrusted client-side checks."

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

In `@src/dbsp_circuit/streams/behaviour/apply.rs` around lines 75 - 105, Update
apply_movement’s movement stream before the base_idx.join call to fold duplicate
decisions by entity into one movement decision, mirroring
dedupe_movement_decisions in decide.rs. Use the deduplicated stream for both
duplicate validation and joining, while preserving the existing warning/debug
assertion behavior.

Source: Coding guidelines

Comment on lines +215 to +240
fn apply(&mut self, movement: &MovementDecision, weight: i64) {
let dx = movement.dx.into_inner();
let dy = movement.dy.into_inner();
#[expect(
clippy::cast_precision_loss,
reason = "Movement counts remain tiny so converting to f64 is exact"
)]
let scaled = weight as f64;
self.sum_dx = OrderedFloat(self.sum_dx.into_inner() + dx * scaled);
self.sum_dy = OrderedFloat(self.sum_dy.into_inner() + dy * scaled);
self.total_weight += weight;
if self.total_weight == 0 {
self.sum_dx = OrderedFloat(0.0);
self.sum_dy = OrderedFloat(0.0);
}
}

fn merge(&mut self, other: &Self) {
self.sum_dx = OrderedFloat(self.sum_dx.into_inner() + other.sum_dx.into_inner());
self.sum_dy = OrderedFloat(self.sum_dy.into_inner() + other.sum_dy.into_inner());
self.total_weight += other.total_weight;
if self.total_weight == 0 {
self.sum_dx = OrderedFloat(0.0);
self.sum_dy = OrderedFloat(0.0);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file and nearby definitions.
file="src/dbsp_circuit/streams/behaviour/decide.rs"
wc -l "$file"
sed -n '1,320p' "$file"

# Search for the accumulator type and any related tests/docs.
rg -n "MovementAccumulator|Fold::with_output|into_decision|total_weight == 0|sum_dx|sum_dy" src docs tests -S

Repository: leynos/lille

Length of output: 11749


🌐 Web query:

DBSP Fold with_output accumulator persists across circuit.step per key semantics

💡 Result:

In the DBSP (Differential Dataflow) computational model, state persistence across circuit steps is governed by the nature of the operators within the dataflow graph [1][2]. When using aggregation operators—which are conceptually equivalent to "fold" operations—DBSP maintains state to support incremental computation [1][3]. 1. State Persistence: DBSP operators that perform accumulations (such as group-by aggregates) are designed to be stateful. This state is preserved across calls to circuit.step [2]. Because DBSP is fundamentally incremental, these operators maintain an internal representation (an accumulator) that reflects the current state of the computation, which is updated with new input changes (deltas) in subsequent steps rather than being recomputed from scratch [1][2]. 2. Accumulator Semantics: The accumulator is effectively an internal state variable. For aggregation functions, this accumulator must implement specific traits (such as MonoidValue, Add, and MulByRef) to ensure that updates can be performed correctly using differential changes [3]. When a new batch of data is processed via circuit.step, the operator applies these updates to the existing state, persisting it automatically until the next step [1][2]. 3. Circuit Steps: A circuit.step call triggers the evaluation of the circuit for a single logical clock tick [4][5]. During this evaluation, the dataflow graph propagates changes, and any stateful operators update their internal accumulators based on the current input deltas [1][2]. The "per key" semantics are inherent to these operators, where the accumulator state is partitioned by the grouping key, ensuring that each key's aggregate is computed and persisted independently [3]. There is no "with_output" parameter that alters this fundamental persistence behavior in the context of the core DBSP library's aggregation operators [2][3]. If you are encountering "with_output" in relation to folding or state, it likely refers to a higher-level framework built on top of DBSP (such as application-specific pipelines or specialized export/manifest logic) rather than a core feature of the DBSP circuit execution model [6][7][8][9]. In standard DBSP, you control output through output handles (e.g., OutputHandle) which allow you to read the state of a stream at the end of a step [10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '200,270p' src/dbsp_circuit/streams/behaviour/decide.rs
printf '\n--- tests/docs ---\n'
rg -n "MovementAccumulator|zero-reset|total_weight == 0|into_decision|Fold::with_output" src docs tests -S

Repository: leynos/lille

Length of output: 4091


🔍 Additional research query:

Deepwiki: dbsp Fold operator accumulator persistent across steps semantics

💡 Result:

Yes—if you mean DBSP/Feldera’s aggregate/fold-style operator, the accumulator is persistent operator state across steps, not something reinitialized each step. DBSP is described as a standing-query engine that updates outputs from input changes while keeping only the state needed for future changes, and Feldera says aggregation operators store both input and output; for user-defined aggregates, the accumulator type must be storable in relations and can be spilled to disk. I infer from that that the accumulator is carried forward across steps as part of the operator’s persistent state. (docs.feldera.com)

Nuance: for linear aggregates like SUM, COUNT, AVG, and STDDEV, Feldera can avoid storing the full input relation, but it still maintains the aggregate state needed for incremental updates. (docs.feldera.com)


Delete the zero-reset in MovementAccumulator::apply and merge.
src/dbsp_circuit/streams/behaviour/decide.rs:215-240into_decision already returns None when total_weight == 0, so clearing sum_dx/sum_dy here only drops additive state and can skew the next update after a zero-crossing.

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

In `@src/dbsp_circuit/streams/behaviour/decide.rs` around lines 215 - 240, Remove
the total_weight == 0 reset blocks from MovementAccumulator::apply and
MovementAccumulator::merge, leaving their additive sum and weight updates
intact. Preserve into_decision’s existing handling of zero total weight.

Comment on lines +11 to +19
trait StreamConcat {
fn concat(&self, other: &Self) -> Self;
}

impl StreamConcat for Stream<RootCircuit, OrdZSet<FearLevel>> {
fn concat(&self, other: &Self) -> Self {
self.plus(other)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Drop the speculative StreamConcat trait; call .plus() directly.

The trait exists purely to rename a single .plus() call at line 87; it adds a layer without a reuse story.

♻️ Proposed fix
-trait StreamConcat {
-    fn concat(&self, other: &Self) -> Self;
-}
-
-impl StreamConcat for Stream<RootCircuit, OrdZSet<FearLevel>> {
-    fn concat(&self, other: &Self) -> Self {
-        self.plus(other)
-    }
-}
-
 /// Merges explicit fear inputs with entity positions, defaulting to zero.

Then replace the trailing call: fears.plus(&missing).

As per coding guidelines, "Introduce abstractions only when they reduce net complexity or have a clearly documented, credible reuse strategy; do not introduce speculative abstractions."
📝 Committable suggestion

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

Suggested change
trait StreamConcat {
fn concat(&self, other: &Self) -> Self;
}
impl StreamConcat for Stream<RootCircuit, OrdZSet<FearLevel>> {
fn concat(&self, other: &Self) -> Self {
self.plus(other)
}
}
/// Merges explicit fear inputs with entity positions, defaulting to zero.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dbsp_circuit/streams/behaviour/fear.rs` around lines 11 - 19, Remove the
speculative StreamConcat trait and its implementation, then update the affected
call site to invoke plus directly on the fears stream with missing as its
argument. Preserve the existing concatenation behavior.

Source: Coding guidelines

Comment on lines +77 to +88
let explicit = fears.clone();

let missing = positions
.map_index(|p| (p.entity, ()))
.antijoin(&explicit.map_index(|f| (f.entity, ())))
.map(|(entity, ())| FearLevel {
entity: *entity,
level: OrderedFloat(0.0),
});

explicit.concat(&missing)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Drop the redundant .clone().

fears is already a reference and both .map_index() and StreamConcat::concat() take &self; cloning buys nothing here.

♻️ Proposed fix
-    let explicit = fears.clone();
-
     let missing = positions
         .map_index(|p| (p.entity, ()))
-        .antijoin(&explicit.map_index(|f| (f.entity, ())))
+        .antijoin(&fears.map_index(|f| (f.entity, ())))
         .map(|(entity, ())| FearLevel {
             entity: *entity,
             level: OrderedFloat(0.0),
         });
 
-    explicit.concat(&missing)
+    fears.concat(&missing)
As per coding guidelines, "Do not use unexplained or avoidable `.clone()` calls to hide ownership problems; prefer borrowing, ownership transfer, narrower scopes, or more precise APIs."
📝 Committable suggestion

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

Suggested change
let explicit = fears.clone();
let missing = positions
.map_index(|p| (p.entity, ()))
.antijoin(&explicit.map_index(|f| (f.entity, ())))
.map(|(entity, ())| FearLevel {
entity: *entity,
level: OrderedFloat(0.0),
});
explicit.concat(&missing)
}
let missing = positions
.map_index(|p| (p.entity, ()))
.antijoin(&fears.map_index(|f| (f.entity, ())))
.map(|(entity, ())| FearLevel {
entity: *entity,
level: OrderedFloat(0.0),
});
fears.concat(&missing)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dbsp_circuit/streams/behaviour/fear.rs` around lines 77 - 88, Remove the
redundant clone in the fear stream construction by binding `explicit` directly
to the borrowed `fears` reference. Keep the existing `map_index` and `concat`
calls unchanged, relying on their `&self` receivers.

Source: Coding guidelines

Comment thread src/dbsp_sync/plugin.rs

#[cfg(test)]
mod tests {
//! Tests for the DBSP synchronisation plugin.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use Oxford spelling consistently in Rust documentation.

Replace initialises/initialisation with initializes/initialization in the affected test documentation.

  • src/dbsp_sync/plugin.rs#L95-L95: update the related test-module wording to use Oxford spelling.
  • src/logging.rs#L58-L58: replace initialisation with initialization.

As per coding guidelines and path instructions, comments and documentation must use en-GB-oxendict spelling.

📍 Affects 2 files
  • src/dbsp_sync/plugin.rs#L95-L95 (this comment)
  • src/logging.rs#L58-L58
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dbsp_sync/plugin.rs` at line 95, Update the affected documentation
wording in src/dbsp_sync/plugin.rs at lines 95-95 and src/logging.rs at lines
58-58, replacing “initialises”/“initialisation” with
“initializes”/“initialization” as specified; no code behavior changes are
needed.

Sources: Coding guidelines, Path instructions

Comment thread src/map/lifecycle.rs
});
}

if asset_path.contains("..") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject traversal components, not filename substrings.

Line 127 rejects valid relative assets such as maps/primary..backup.tmx. Split on / and reject only a segment equal to ..; add that filename as an accepted case.

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

In `@src/map/lifecycle.rs` at line 127, Update the asset path validation around
the visible contains check to split the path on `/` and reject only when a
component equals `..`, allowing filenames such as `maps/primary..backup.tmx`;
add a test covering this accepted case.

Comment thread src/map/lifecycle.rs
Comment on lines +190 to +193
#[expect(
clippy::too_many_arguments,
reason = "Bevy observer systems require query parameters; grouping would obscure intent."
)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

🧩 Analysis chain

🏁 Script executed:

sed -n '160,340p' src/map/lifecycle.rs

Repository: leynos/lille

Length of output: 6547


Link or remove these #[expect] suppressions
src/map/lifecycle.rs:190-193, 231-234, and 312-315 all need an issue or roadmap reference; keep them only as tracked in-progress work, or drop them.

📍 Affects 1 file
  • src/map/lifecycle.rs#L190-L193 (this comment)
  • src/map/lifecycle.rs#L231-L234
  • src/map/lifecycle.rs#L312-L315
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/map/lifecycle.rs` around lines 190 - 193, Update the
#[expect(clippy::too_many_arguments)] suppressions in src/map/lifecycle.rs at
lines 190-193, 231-234, and 312-315: either add an issue or roadmap reference to
each suppression to track the in-progress work, or remove the suppressions if no
tracking reference exists.

Sources: Coding guidelines, Path instructions

Comment thread src/map/lifecycle.rs
Comment on lines +295 to +301
fn build_spawn_rejects_invalid_path_without_spawning() {
let mut app = app_with_settings(true, "/absolute/path.tmx");
try_spawn_primary_map_on_build(&mut app);
let world = app.world_mut();
let mut maps = world.query_filtered::<Entity, With<PrimaryTiledMap>>();
assert!(maps.iter(world).next().is_none());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant section of the file with line numbers.
sed -n '1,380p' src/map/lifecycle.rs | cat -n

echo
echo '--- SEARCH InvalidPrimaryMapAssetPath ---'
rg -n "InvalidPrimaryMapAssetPath|try_spawn_primary_map_on_build|PrimaryTiledMap|AssetServer" src -S

Repository: leynos/lille

Length of output: 16763


Make this build test assert rejection. Register an observer for InvalidPrimaryMapAssetPath and assert it fires; checking only for “no spawned map” still passes if try_spawn_primary_map_on_build returns early because AssetServer is absent.

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

In `@src/map/lifecycle.rs` around lines 295 - 301, Update
build_spawn_rejects_invalid_path_without_spawning to register an observer for
InvalidPrimaryMapAssetPath before calling try_spawn_primary_map_on_build, then
assert the observer was triggered. Retain the existing assertion that no
PrimaryTiledMap entity is spawned, ensuring the test verifies both rejection and
absence of a map.

Sources: Coding guidelines, Path instructions

Comment thread src/map/lifecycle.rs Outdated
Comment on lines +325 to +327
let Some(handle) = tracking.handle.clone() else {
return;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- src/map/lifecycle.rs (around lines 300-360) ---'
sed -n '300,360p' src/map/lifecycle.rs

printf '\n%s\n' '--- search for handle type / id() usages ---'
rg -n "handle\.id\(\)|tracking\.handle|struct .*Handle|type .*Handle|enum .*Handle" src -g '!**/target/**'

Repository: leynos/lille

Length of output: 2351


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- src/map/lifecycle.rs (top and relevant sections) ---'
sed -n '1,140p' src/map/lifecycle.rs
printf '\n%s\n' '--- src/map/lifecycle.rs (handle query section) ---'
sed -n '236,262p' src/map/lifecycle.rs

printf '\n%s\n' '--- handle type definition / imports in file ---'
rg -n "use .*Handle|struct PrimaryMapAssetTracking|handle:" src/map/lifecycle.rs

Repository: leynos/lille

Length of output: 5895


Borrow the tracked handle instead of cloning it.
src/map/lifecycle.rs:252 — Bind tracking.handle.as_ref() and call handle.id(). Cloning here bumps the strong count on every monitor tick without using the extra ownership.

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

In `@src/map/lifecycle.rs` around lines 325 - 327, Update the handle binding in
the tracking lifecycle logic to borrow via tracking.handle.as_ref() instead of
cloning the Option or handle. Preserve the early return when no handle is
present, and continue obtaining the identifier through handle.id().

Source: Coding guidelines

lodyai Bot pushed a commit that referenced this pull request Jul 19, 2026
Act on the review comments left on the Whitaker Dylint adoption (#269),
verifying each finding against the current tree before changing it.

- CI: reuse a cached whitaker-installer only when its --version matches the
  pinned WHITAKER_INSTALLER_VERSION rather than trusting `command -v` alone.
- behaviour: dedupe movement decisions per entity inside apply_movement before
  the join (sharing decide.rs's dedupe helper) and add end-to-end tests;
  drop the total_weight == 0 reset blocks; remove the speculative StreamConcat
  wrapper and a redundant fears clone; reuse build_decision_circuit and drop
  the unused_mut expect in the tests.
- dbsp_sync/output: replace the apply_output_records! macro with explicit
  resolve_write_target/resolve_health_target helpers, skip non-positive Z-set
  weights before mutating ECS, clear circuit inputs on the step-error path, and
  cover the failure and negative-weight paths with tests.
- map/lifecycle: reject only whole `..` path components (accepting names such as
  maps/primary..backup.tmx), assert InvalidPrimaryMapAssetPath via an observer,
  and borrow tracking.handle instead of cloning it.
- docs: use Oxford -ize spelling for initialize/initialization in plugin and
  logging prose.
- Makefile: add `set -euo pipefail` to the lint recipe so a failing Clippy step
  no longer hides behind the .ONESHELL exit code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
leynos added a commit that referenced this pull request Jul 22, 2026
Installer 0.2.7 stops executing `dylint-link` as a health check
(leynos/whitaker#299), so the verified repository artefact is accepted
instead of triggering a source build that cannot compile on this
repository's pinned toolchain. Adoption PR #269 merged with the 0.2.6 pin,
which left the lint gate on `main` unable to install the suite.

Co-authored-by: leynos <leynos@rohga>
lodyai Bot pushed a commit that referenced this pull request Jul 25, 2026
Act on the review comments left on the Whitaker Dylint adoption (#269),
verifying each finding against the current tree before changing it.

- CI: reuse a cached whitaker-installer only when its --version matches the
  pinned WHITAKER_INSTALLER_VERSION rather than trusting `command -v` alone.
- behaviour: dedupe movement decisions per entity inside apply_movement before
  the join (sharing decide.rs's dedupe helper) and add end-to-end tests;
  drop the total_weight == 0 reset blocks; remove the speculative StreamConcat
  wrapper and a redundant fears clone; reuse build_decision_circuit and drop
  the unused_mut expect in the tests.
- dbsp_sync/output: replace the apply_output_records! macro with explicit
  resolve_write_target/resolve_health_target helpers, skip non-positive Z-set
  weights before mutating ECS, clear circuit inputs on the step-error path, and
  cover the failure and negative-weight paths with tests.
- map/lifecycle: reject only whole `..` path components (accepting names such as
  maps/primary..backup.tmx), assert InvalidPrimaryMapAssetPath via an observer,
  and borrow tracking.handle instead of cloning it.
- docs: use Oxford -ize spelling for initialize/initialization in plugin and
  logging prose.
- Makefile: add `set -euo pipefail` to the lint recipe so a failing Clippy step
  no longer hides behind the .ONESHELL exit code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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