feat: Add multi-agent infrastructure (Phase 4 Deliverable 1) - #108
Conversation
Extract per-agent mutable state (position, body, direction, HP, visited_cells, wall_collision_occurred, comfort zone counters) into AgentState dataclass. Environment maintains agents dict keyed by agent_id. Single-agent mode uses "default" agent with backward-compatible property accessors. All 188 existing tests pass unchanged. Phase 4 Deliverable 1, Phase 1: AgentState Extraction (tasks 1.1-1.6) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
âĶarams Add SOCIAL_PROXIMITY module (classical_dim=1) for multi-agent population density sensing. Normalized count of nearby agents: min(count, 10) / 10.0. Add nearby_agents_count field to BrainParams (None in single-agent mode). Handle classical_dim=1 in SensoryModule.to_classical(). 14 new tests. Phase 4 Deliverable 1, Phase 4: BrainParams Social Field (tasks 4.1-4.4) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactor movement into _apply_movement(agent_state, action) with _get_new_position_from(position, direction) for multi-agent support. Add *_for variants for: move_agent, reached_goal, consume_food, is_agent_in_danger, is_agent_in_damage_radius, is_agent_at_boundary, apply_predator_damage, apply_temperature_effects, apply_oxygen_effects. Add thin wrappers for methods already accepting position parameter: get_separated_gradients, get_food_concentration, get_predator_concentration, get_temperature, get_oxygen, etc. Add add_agent() method. All 394 env/agent tests pass unchanged. Phase 4 Deliverable 1, Phase 2: Position-Parameterized Methods (tasks 2.1-2.8) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pursuit predators chase nearest alive agent via new agent_positions parameter. update_predators() passes all alive agent positions. Single-agent behavior unchanged. 17 new tests: AgentState properties, add_agent, independent movement, predator multi-target pursuit, target switching, stationary predator unaffected. 411 env/agent tests pass. Phase 4 Deliverable 1, Phase 3: Predator Multi-Target (tasks 3.1-3.3) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
âĶ awareness New multi_agent.py: MultiAgentSimulation orchestrator with synchronous step loop (perception -> movement -> food competition -> predators -> effects -> learning). FoodCompetitionPolicy (FIRST_ARRIVAL, RANDOM), resolve_food_competition(), validate_multi_agent_grid(), MultiAgentEpisodeResult with Gini coefficient. Agent gets agent_id param and nearby_agents_count injection in _create_brain_params. 17 new tests covering food competition, grid validation, Gini, 2/5-agent episodes, termination policies, and per-agent food tracking. 428 env/agent tests pass. Phase 4 Deliverable 1, Phases 5-8 (tasks 5.1-8.3) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AgentConfig, MultiAgentConfig with count/agents mutual exclusion validator. multi_agent field on SimulationConfig. Export MultiAgentSimulation, FoodCompetitionPolicy, MultiAgentEpisodeResult from agent __init__. 4 scenario configs: 2-agent, 5-agent, 10-agent homogeneous MLP PPO, and 3-agent mixed brains (mlpppo + lstmppo + qrh). 1982 tests pass. Phase 4 Deliverable 1, Phases 9+12 (tasks 9.1-9.5, 12.1-12.5) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
âĶm_container
Multi-agent branch in run_simulation.py: detects multi_agent.enabled, creates
N agents with independent brains in shared env, runs episodes via
MultiAgentSimulation, per-agent weight save as final_{agent_id}.pt.
configure_brain_from_container() helper for per-agent brain config from YAML.
_configure_brain_for_agent() applies sensing mode translation per agent.
Supports both homogeneous (count) and heterogeneous (agents list) configs.
Phase 4 Deliverable 1, Phases 10+11 (tasks 10.1-11.4)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
âĶntation All 1982 tests pass. Ruff lint clean. Update roadmap Phase 4 status to IN PROGRESS with Deliverable 1 complete. Strike "Single-agent only" from Known Gaps. Add multi_agent_foraging to AGENTS.md scenario list. Fix test lint: keyword-only bool arg, match pattern on raises, unused var. Ruff format applied to multi_agent.py. Phase 4 Deliverable 1, Phase 13: Verification and Documentation (tasks 13.1-13.12) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use ActionData type instead of object for action dicts in multi_agent.py - Fix tuple[int, ...] â tuple[int, int] in AgentState body/position assignments - Use ModuleName enum in test fixtures instead of string literals - Add BrainConfigType import and proper return types in run_simulation.py - Import AdamLearningRate/PerformanceBasedLearningRate for full union type - Guard config.brain None check with explicit ValueError for multi_agent.count - Initialize config variable before conditional block to fix possibly-unbound - 0 pyright errors, 442 tests pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
âĶcursion B1: Replace agent.reset_environment() in multi-agent loop with proper shared env recreation â all agents now point to the same fresh env. B2: _alive_agents property safely handles removed agents (checks agent_id in env.agents before accessing .alive). S1: end_all termination uses iterative _terminate_single() instead of recursive _handle_termination() to avoid stack depth issues. S2: Validate termination_policy in __post_init__ against allowed values. S3: Guard predator damage loop against removed agents. Clean up redundant noqa: SLF001 directives across scripts (now per-file). 2 new tests: invalid policy raises, remove policy no KeyError. 19/19 pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
âĶomment I1: Update openspec/config.yaml Architecture section to mention multi-agent support and MultiAgentSimulation orchestrator. I2: Remove unused reward_config param from _resolve_food_step (was suppressed with noqa: ARG002 â cleaner to remove entirely). M1: Add clarifying comment on DEFAULT_AGENT_ID skip in proximity count. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
agents_alive_at_end was off by 1 because the backward-compat 'default' AgentState (always alive=True) was counted. Now excludes DEFAULT_AGENT_ID. Found during sanity check: 2-agent runs showed alive=1/2 when both starved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Small grid forces agents into food competition events. Reduced min_food_distance=2, agent_exclusion_radius=2, gradient_decay=4.0, max_steps=300 for faster episodes. For functional verification of food competition resolution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Configs with multi_agent.agents (per-agent brains, no top-level brain) crashed with "No brain configuration found" because configure_brain() was called before the multi-agent branch. Now detects heterogeneous multi-agent early and skips single-agent brain/sensing configuration. Found during mixed_brains evaluation run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 67 tasks audited against implementation. 64 fully done, 3 deferred: - 10.3: Weight save/load round-trip test (verified via sanity check) - 11.3: CSV export with agent_id column (console output sufficient) - 11.4: End-of-session summary table (per-episode output works) Preparing for archival. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 4 Deliverable 1 complete. 64/67 tasks done, 3 deferred (CSV export, weight round-trip test, session summary table). Infrastructure validated across 20 experiment runs (5 scenarios x 4 seeds): backward compat 94.3%, 2/5-agent foraging, competition stress test, mixed brains (mlpppo+lstmppo+qrh). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ð WalkthroughWalkthroughAdds multi-agent infrastructure: per-agent AgentState and APIs, SOCIAL_PROXIMITY sensory module and BrainParams.nearby_agents_count, deterministic/random food-competition, MultiAgentSimulation orchestrator, configuration/schema and CLI integration, scenario YAMLs, tests, and exports for 2â10 agents sharing a DynamicForagingEnvironment. Changes
Sequence Diagram(s)sequenceDiagram
participant MA as MultiAgentSimulation
participant Env as DynamicForagingEnvironment
participant Agent as QuantumNematodeAgent
participant Pred as Predator
MA->>Env: reset() / add agents
MA->>Agent: prepare_episode() each
loop timestep
par per-agent perception & action
MA->>Env: get_sensory_for(agent_id)
Env-->>MA: sensory + nearby_agents_count
MA->>Agent: get_action(agent_id, sensory)
Agent-->>MA: action
end
par apply movements
MA->>Env: move_agent_for(agent_id, action) each
end
MA->>MA: collect contested food cells
MA->>MA: resolve_food_competition(contested, policy)
MA->>Env: consume_food_for(winning_agent)
MA->>Pred: update_predators(agent_positions)
Pred-->>Env: pursue nearest-agent
par per-agent effects & termination
MA->>Env: apply_predator_damage_for(agent_id)
MA->>Env: apply_temperature_effects_for(agent_id)
MA->>MA: handle_termination(agent_id)
end
MA->>Agent: learn/post_process per agent as needed
end
MA->>MA: build MultiAgentEpisodeResult (per-agent + aggregate metrics)
Estimated code review effortðŊ 4 (Complex) | âąïļ ~60 minutes Possibly related PRs
Poem
ðĨ Pre-merge checks | â 3â Passed checks (3 passed)
âïļ Tip: You can configure your own custom pre-merge checks in the settings. âĻ Finishing Touchesð Generate docstrings
ð§Š Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Reportâ Patch coverage is ðĒ Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 7
ð§đ Nitpick comments (2)
packages/quantum-nematode/quantumnematode/agent/agent.py (1)
159-159: Use the shared default agent identifier constant instead of a string literal.Line 159 hardcodes
"default". Prefer reusingDEFAULT_AGENT_IDto avoid drift between environment and agent defaults.âŧïļ Proposed refactor
from quantumnematode.env import ( + DEFAULT_AGENT_ID, DynamicForagingEnvironment, ) @@ - agent_id: str = "default", + agent_id: str = DEFAULT_AGENT_ID, @@ - Unique identifier for multi-agent mode. Defaults to "default". + Unique identifier for multi-agent mode. Defaults to DEFAULT_AGENT_ID.Also applies to: 183-183
ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/agent/agent.py` at line 159, Replace the hardcoded "default" agent id string with the shared constant DEFAULT_AGENT_ID: update the agent_id parameter default in the relevant function signatures (the occurrence at agent.py where agent_id: str = "default" and the other occurrence around the second location) to use DEFAULT_AGENT_ID, and ensure DEFAULT_AGENT_ID is imported or referenced in that module so both places use the same canonical default identifier.packages/quantum-nematode/tests/quantumnematode_tests/brain/test_social_module.py (1)
58-70: Add a negative-count regression test for social proximity normalization.Current tests cover upper clamping but not lower-bound safety. Add one case (
nearby_agents_count=-1) to lock in[0, 1]normalization behavior.ð§Š Suggested test addition
class TestSocialProximityModule: @@ def test_clamped_above_ten(self) -> None: """Test that counts above 10 are clamped to 1.0.""" module = SENSORY_MODULES[ModuleName.SOCIAL_PROXIMITY] params = BrainParams(nearby_agents_count=15) features = module.to_classical(params) assert features[0] == pytest.approx(1.0) + + def test_clamped_below_zero(self) -> None: + """Test that negative counts are clamped to 0.0.""" + module = SENSORY_MODULES[ModuleName.SOCIAL_PROXIMITY] + params = BrainParams(nearby_agents_count=-1) + features = module.to_classical(params) + assert features[0] == pytest.approx(0.0)ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/tests/quantumnematode_tests/brain/test_social_module.py` around lines 58 - 70, Add a regression test that ensures negative nearby_agents_count values are clamped to the [0,1] range: create a test similar to test_one_nearby_agent but instantiate BrainParams(nearby_agents_count=-1), call SENSORY_MODULES[ModuleName.SOCIAL_PROXIMITY].to_classical(params) and assert the returned feature at index 0 equals pytest.approx(0.0); reference SENSORY_MODULES, ModuleName.SOCIAL_PROXIMITY, BrainParams, and to_classical to locate where to add the test.
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/quantum-nematode/quantumnematode/agent/multi_agent.py`:
- Around line 306-320: The code computes nearby_agents_count (nearby) when
selecting actions but later rebuilds BrainParams without it before calling
learn(), causing SOCIAL_PROXIMITY to be missing during updates; fix by threading
the cached nearby value into every call that constructs BrainParams for learning
(use agent._create_brain_params(..., nearby_agents_count=nearby) rather than
reconstructing params without that arg) in the termination code paths
(specifically in _handle_termination() and _terminate_single() where
episode_done=True learns are invoked and where action_per_agent and nearby were
originally computed) so that the same nearby_agents_count used during action
selection is passed to learn() via BrainParams.
- Around line 202-234: In __post_init__, after validating each agent.env is
self.env and before initializing _per_agent_food, add a guard that verifies
every agent.agent_id exists in self.env.agents and raise a ValueError if any are
missing; e.g., compute missing_ids = [a.agent_id for a in self.agents if
a.agent_id not in self.env.agents] and if missing_ids: raise ValueError with a
clear message listing missing_ids and the full list of provided agent IDs so
MultiAgentSimulation fails fast on misregistered env IDs (refer to symbols:
__post_init__, self.agents, self.env.agents, _per_agent_food).
In `@packages/quantum-nematode/quantumnematode/brain/modules.py`:
- Around line 778-780: The current computation for social proximity uses count =
params.nearby_agents_count and normalized = min(count, 10) / 10.0 which only
clamps the upper bound; if nearby_agents_count is negative strength can be <0.
Replace that logic in the block around params.nearby_agents_count so that you
clamp both bounds (e.g. clamp count to [0,10] using max/min) before computing
normalized and returning CoreFeatures(strength=normalized, angle=0.0,
binary=0.0).
In `@packages/quantum-nematode/quantumnematode/utils/config_loader.py`:
- Around line 750-770: MultiAgentConfig's _validate_population currently only
checks presence of count vs agents and allows invalid sizes; update
_validate_population to enforce that when enabled is True either count is set to
an int in the inclusive range 2..10 or agents is a non-empty list whose length
is in the inclusive range 2..10, otherwise raise ValueError with clear messages
(e.g. "multi_agent.count must be between 2 and 10" or "multi_agent.agents must
contain between 2 and 10 entries"); ensure you check for non-positive counts
(<=0) and also validate that if agents is provided its length meets the same
2..10 constraint before returning self.
In `@scripts/run_simulation.py`:
- Around line 1553-1559: The environment is being recreated with the global
simulation_seed instead of the per-episode run_seed so derive_run_seed() has no
effect on environment RNG; change all calls that build or rebuild the env (calls
to create_env_from_config) to pass the per-episode run_seed (the value returned
by derive_run_seed or the local run_seed variable) instead of simulation_seed so
agent spawn, food/predator placement and any RANDOM policies are seeded per-run;
update every occurrence where create_env_from_config is invoked during episode
setup/rebuild (including the initial creation and in the per-episode
reset/rebuild code paths) to use run_seed.
- Around line 1487-1510: The multi-agent path hardcodes perf_mgmt=None when
calling setup_brain_model inside _run_multi_agent, so the --optimize flag is
ignored; fix this by adding a perf_mgmt parameter to _run_multi_agent (matching
the object passed by main), update its signature to accept the same perf_mgmt
type used by main, propagate that parameter through any callers of
_run_multi_agent, and pass the live perf_mgmt instance into setup_brain_model
instead of None so multi-agent QPU runs use the same optimization management as
the single-agent path (see how single-agent passes perf_mgmt into
setup_brain_model for reference).
- Around line 1565-1566: Replace the salted, interpreter-dependent Python hash
usage for per-agent seed derivation with a stable cryptographic hash: add
"import hashlib" to the stdlib imports, then compute a blake2s (or similar)
digest over the string or bytes representation of (simulation_seed, ac.id),
convert the digest to an integer (e.g., from hex or bytes) and take modulo 2**32
to produce agent_seed; update the code path that sets agent_seed (the current
line using hash((simulation_seed, ac.id)) % (2**32)) to use this deterministic
blake2s-based conversion so seeds are reproducible across runs.
---
Nitpick comments:
In `@packages/quantum-nematode/quantumnematode/agent/agent.py`:
- Line 159: Replace the hardcoded "default" agent id string with the shared
constant DEFAULT_AGENT_ID: update the agent_id parameter default in the relevant
function signatures (the occurrence at agent.py where agent_id: str = "default"
and the other occurrence around the second location) to use DEFAULT_AGENT_ID,
and ensure DEFAULT_AGENT_ID is imported or referenced in that module so both
places use the same canonical default identifier.
In
`@packages/quantum-nematode/tests/quantumnematode_tests/brain/test_social_module.py`:
- Around line 58-70: Add a regression test that ensures negative
nearby_agents_count values are clamped to the [0,1] range: create a test similar
to test_one_nearby_agent but instantiate BrainParams(nearby_agents_count=-1),
call SENSORY_MODULES[ModuleName.SOCIAL_PROXIMITY].to_classical(params) and
assert the returned feature at index 0 equals pytest.approx(0.0); reference
SENSORY_MODULES, ModuleName.SOCIAL_PROXIMITY, BrainParams, and to_classical to
locate where to add the test.
ðŠ 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4927c653-ccb3-4222-a37e-ca5e8ee60cf2
ð Files selected for processing (33)
AGENTS.mdconfigs/scenarios/multi_agent_foraging/mixed_brains_medium_3agents_oracle.ymlconfigs/scenarios/multi_agent_foraging/mlpppo_large_10agents_oracle.ymlconfigs/scenarios/multi_agent_foraging/mlpppo_medium_2agents_oracle.ymlconfigs/scenarios/multi_agent_foraging/mlpppo_medium_5agents_oracle.ymlconfigs/scenarios/multi_agent_foraging/mlpppo_small_5agents_competition_oracle.ymldocs/roadmap.mdopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/.openspec.yamlopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/design.mdopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/proposal.mdopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/specs/brain-architecture/spec.mdopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/specs/configuration-system/spec.mdopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/specs/environment-simulation/spec.mdopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/specs/multi-agent/spec.mdopenspec/changes/archive/2026-04-05-add-multi-agent-infrastructure/tasks.mdopenspec/config.yamlpackages/quantum-nematode/quantumnematode/agent/__init__.pypackages/quantum-nematode/quantumnematode/agent/agent.pypackages/quantum-nematode/quantumnematode/agent/multi_agent.pypackages/quantum-nematode/quantumnematode/brain/arch/_brain.pypackages/quantum-nematode/quantumnematode/brain/modules.pypackages/quantum-nematode/quantumnematode/env/__init__.pypackages/quantum-nematode/quantumnematode/env/env.pypackages/quantum-nematode/quantumnematode/utils/config_loader.pypackages/quantum-nematode/tests/quantumnematode_tests/agent/test_multi_agent.pypackages/quantum-nematode/tests/quantumnematode_tests/brain/test_social_module.pypackages/quantum-nematode/tests/quantumnematode_tests/env/test_env.pypyproject.tomlscripts/export_screenshot.pyscripts/qef_mi_analysis.pyscripts/qrh_mi_analysis.pyscripts/run_plasticity_test.pyscripts/run_simulation.py
âĶ perf_mgmt Fix nearby_agents_count missing in BrainParams during learn() and termination: cache per-agent nearby count and thread through all _create_brain_params calls in learning phase, _handle_termination, and _terminate_single. Add agent_id registration validation in __post_init__: verify every agent's agent_id exists in env.agents, fail fast with clear message listing missing IDs. Clamp social proximity count to [0,10] (was only upper-bounded; negative values could produce negative strength). Add regression test. Add count/agents range validation (2-10) in MultiAgentConfig validator. Fix env reset seed: use per-episode run_seed instead of simulation_seed so food/predator/agent placement varies per episode. Pass perf_mgmt through _run_multi_agent instead of hardcoding None, so --optimize flag works for multi-agent QPU runs. Replace Python hash() with hashlib.blake2b for stable per-agent seed derivation (hash() is non-deterministic with PYTHONHASHSEED randomization). Use DEFAULT_AGENT_ID constant in agent.py instead of "default" string literal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
âŧïļ Duplicate comments (1)
scripts/run_simulation.py (1)
1556-1562:â ïļ Potential issue | ð MajorSeed the environment for the episode that is actually running.
The environment created at Line 1557 still uses
simulation_seed, and the rebuild at Line 1656 uses the current episode'srun_seedonly after that episode finishes. The first run therefore executes on the wrong environment seed, and every later run is one episode behind.ð ïļ Suggested fix
- # Create shared environment + # Create shared environment for run 0 + initial_run_seed = derive_run_seed(simulation_seed, 0) env = create_env_from_config( environment_config, - seed=simulation_seed, + seed=initial_run_seed, max_body_length=body_length, theme=theme, )- # Reset for next episode: create fresh shared env with per-episode seed - env = create_env_from_config( - environment_config, - seed=run_seed, - max_body_length=body_length, - theme=theme, - ) + # Prepare the next episode with its own derived seed + if run + 1 >= runs: + continue + next_run_seed = derive_run_seed(simulation_seed, run + 1) + env = create_env_from_config( + environment_config, + seed=next_run_seed, + max_body_length=body_length, + theme=theme, + )Also applies to: 1631-1661
ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/run_simulation.py` around lines 1556 - 1562, The environment is being created with the global simulation_seed (env = create_env_from_config(..., seed=simulation_seed,...)) instead of the per-episode seed, causing the first episode to run with the wrong seed and subsequent episodes to be one behind; update the create_env_from_config calls (and any rebuild logic that currently uses simulation_seed) to pass the current episode's run_seed (seed=run_seed) when initializing or rebuilding the env so each episode uses its intended seed (also apply the same change to the repeated block referenced around the rebuild in the 1631-1661 region).
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/run_simulation.py`:
- Around line 399-421: The multi-agent branch early-circuits before any weight
load/save handling, causing --load-weights/--save-weights to be ignored; update
the conditional where multi_agent_config is checked so you either (A) propagate
the weight flags into _run_multi_agent by adding parameters for load_weights and
save_weights (and any related paths/flags) to the _run_multi_agent(...) call and
update the _run_multi_agent signature to handle loading/saving, or (B) perform a
fast-fail: check if load_weights or save_weights are set when
multi_agent_config.enabled is true and raise a clear error before calling
_run_multi_agent; reference multi_agent_config, _run_multi_agent, and the CLI
flags --load-weights/--save-weights when making the change.
- Around line 1664-1674: The multi-agent initialization loop sets env, path,
food_history, _food_handler.env and resets
_satiety_manager/_food_handler/_episode_tracker but omits clearing agent
recurrent state; call agent.reset_brain() for each agent inside the for agent in
agents: loop (after or immediately after agent._episode_tracker.reset()) so
recurrent state, episode buffers and history are cleared between episodes;
ensure you add this call alongside the existing per-agent resets to mirror the
single-agent logic that invokes reset_brain().
---
Duplicate comments:
In `@scripts/run_simulation.py`:
- Around line 1556-1562: The environment is being created with the global
simulation_seed (env = create_env_from_config(..., seed=simulation_seed,...))
instead of the per-episode seed, causing the first episode to run with the wrong
seed and subsequent episodes to be one behind; update the create_env_from_config
calls (and any rebuild logic that currently uses simulation_seed) to pass the
current episode's run_seed (seed=run_seed) when initializing or rebuilding the
env so each episode uses its intended seed (also apply the same change to the
repeated block referenced around the rebuild in the 1631-1661 region).
ðŠ 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: d10e9bbf-fcf0-4438-adf6-e043006bc57d
ð Files selected for processing (6)
packages/quantum-nematode/quantumnematode/agent/agent.pypackages/quantum-nematode/quantumnematode/agent/multi_agent.pypackages/quantum-nematode/quantumnematode/brain/modules.pypackages/quantum-nematode/quantumnematode/utils/config_loader.pypackages/quantum-nematode/tests/quantumnematode_tests/brain/test_social_module.pyscripts/run_simulation.py
ð§ Files skipped from review as they are similar to previous changes (4)
- packages/quantum-nematode/quantumnematode/utils/config_loader.py
- packages/quantum-nematode/quantumnematode/agent/agent.py
- packages/quantum-nematode/tests/quantumnematode_tests/brain/test_social_module.py
- packages/quantum-nematode/quantumnematode/agent/multi_agent.py
âĶtween episodes CLI --load-weights/--save-weights are single-agent concepts (one brain). Multi-agent uses per-agent weights_path in config. Raise clear ValueError if these flags are used with multi_agent.enabled. Add agent.reset_brain() to multi-agent episode reset loop to clear brain.history_data between episodes, matching single-agent behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
ð§đ Nitpick comments (1)
scripts/run_simulation.py (1)
1692-1695: Consider catching more specific exceptions.The bare
Exceptioncatch works for graceful degradation during cleanup, but narrowing to expected failure modes (e.g.,OSError | TypeError) would avoid masking unexpected errors.âŧïļ Optional refinement
- except Exception: + except (OSError, TypeError): logger.exception("Failed to save weights for %s", agent.agent_id)ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/run_simulation.py` around lines 1692 - 1695, The current bare "except Exception" around save_weights(agent.brain, weights_dir / weight_file) can mask unexpected errors; replace it with a specific exception handler (e.g., "except (OSError, TypeError) as e") to catch the expected failure modes when writing files or serializing the brain, call logger.exception("Failed to save weights for %s: %s", agent.agent_id, e) (or keep logger.exception with the exception) and remove the broad catch so other unexpected exceptions will propagate (or add an explicit re-raise for non-expected exceptions).
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/run_simulation.py`:
- Around line 1639-1682: The environment seed is lagging by one run because env
is recreated after the episode; fix by creating the per-episode env at the start
of each loop using derive_run_seed(simulation_seed, run) and passing that
run_seed into create_env_from_config before calling set_global_seed and
sim.run_episode; also change the initial env creation to use
derive_run_seed(simulation_seed, 0) instead of simulation_seed; remove the env
recreation block at the end of the loop and ensure agent.env, agent.path,
agent.food_history, agent._food_handler.env, and resets
(agent._satiety_manager.reset(), agent._food_handler.reset(),
agent._episode_tracker.reset(), agent.reset_brain()) are applied right after the
new env is created (use the same symbols: derive_run_seed, set_global_seed,
create_env_from_config, sim.run_episode, agent.reset_brain).
---
Nitpick comments:
In `@scripts/run_simulation.py`:
- Around line 1692-1695: The current bare "except Exception" around
save_weights(agent.brain, weights_dir / weight_file) can mask unexpected errors;
replace it with a specific exception handler (e.g., "except (OSError, TypeError)
as e") to catch the expected failure modes when writing files or serializing the
brain, call logger.exception("Failed to save weights for %s: %s",
agent.agent_id, e) (or keep logger.exception with the exception) and remove the
broad catch so other unexpected exceptions will propagate (or add an explicit
re-raise for non-expected exceptions).
ðŠ 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: f02cb790-810d-4112-a781-f914d519bd4b
ð Files selected for processing (1)
scripts/run_simulation.py
Env was recreated at the end of each episode with the current run_seed, but used for the next episode â causing run N to use run N-1's seed. Now create fresh env at the start of each iteration (run > 0) with the correct run_seed. Initial env uses derive_run_seed(simulation_seed, 0) instead of bare simulation_seed for consistency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/run_simulation.py`:
- Around line 1603-1607: The runner ignores MultiAgentConfig.min_agent_distance
because env.add_agent is called with position=None; update both the initial
placement (around the env.add_agent call at lines ~1603) and the per-run rebuild
(around lines ~1652-1653) to honor config.min_agent_distance: compute a concrete
spawn position that is at least config.min_agent_distance from all existing
agents (using any existing helper like find_spawn_position/select_spawn_position
or by sampling/validating positions against env.get_agent_positions()) and pass
that position into env.add_agent(position=spawn_pos,
max_body_length=body_length); if env.add_agent already supports a min_distance
parameter, pass config.min_agent_distance directly instead. Ensure you reference
MultiAgentConfig.min_agent_distance, env.add_agent, and the initial
placement/rebuild code paths when making the change.
- Around line 1598-1600: The multi-agent branch calls load_weights(agent_brain,
Path(ac.weights_path)) without verifying the agent_brain supports weight
persistence; update the branch to mirror the single-agent guard by checking
isinstance(agent_brain, WeightPersistence) before calling load_weights so
heterogeneous agents won't raise unclear errorsâuse the ac.weights_path,
agent_brain, load_weights, and WeightPersistence symbols to locate and modify
the conditional.
- Around line 1556-1557: The multi-agent branch only calls
validate_multi_agent_grid(...) and skips the qubit/classical checks in
validate_simulation_parameters(...), allowing non-default qubits into classical
brains; update the multi-agent path to also run the same validation by invoking
validate_simulation_parameters(...) (or extract and call the specific
qubit/classical validation helper used by validate_simulation_parameters) after
validate_multi_agent_grid(...) so the qubit vs classical brain guard is enforced
for multi-agent configs as well.
- Around line 399-429: The multi-agent early-return currently only rejects
--load-weights/--save-weights but lets single-agent-only CLI flags silently
noop; update the conditional just before calling _run_multi_agent to also check
args.manyworlds, args.track_per_run, args.track_experiment, and
args.validate_chemotaxis and raise a ValueError with a clear message (same style
as the existing msg) telling the user these flags cannot be used with
multi-agent mode and to use per-agent equivalents in multi_agent.agents; keep
the error handling co-located with the existing load/save weights check so all
single-agent-only flags fail fast before calling _run_multi_agent.
ðŠ 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: b85bb1b4-65ca-4287-a385-c53f3d8edd5f
ð Files selected for processing (1)
scripts/run_simulation.py
| # Validate grid size | ||
| validate_multi_agent_grid(grid_size, num_agents) |
There was a problem hiding this comment.
Preserve the existing qubit/classical validation in multi-agent mode.
Because this path returns before validate_simulation_parameters() runs, the only validation here is grid capacity. A multi-agent config can now pass non-default qubits into classical brains without the guard the single-agent path already has.
ð§ Minimal fix
# Validate grid size
validate_multi_agent_grid(grid_size, num_agents)
+ for ac in agent_configs:
+ validate_simulation_parameters(
+ maze_grid_size=grid_size,
+ brain_type=BrainType(ac.brain.name),
+ qubits=qubits,
+ )
logger.info(f"Multi-agent mode: {num_agents} agents, grid {grid_size}x{grid_size}")ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/run_simulation.py` around lines 1556 - 1557, The multi-agent branch
only calls validate_multi_agent_grid(...) and skips the qubit/classical checks
in validate_simulation_parameters(...), allowing non-default qubits into
classical brains; update the multi-agent path to also run the same validation by
invoking validate_simulation_parameters(...) (or extract and call the specific
qubit/classical validation helper used by validate_simulation_parameters) after
validate_multi_agent_grid(...) so the qubit vs classical brain guard is enforced
for multi-agent configs as well.
| env.add_agent( | ||
| agent_id=ac.id, | ||
| position=None, # Random valid position | ||
| max_body_length=body_length, | ||
| ) |
There was a problem hiding this comment.
min_agent_distance is effectively a no-op right now.
MultiAgentConfig exposes min_agent_distance, but both the initial placement and the per-run rebuild still call env.add_agent(..., position=None, max_body_length=body_length) without consuming it. Configs that rely on separated spawns will be ignored in this runner.
Also applies to: 1652-1653
ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/run_simulation.py` around lines 1603 - 1607, The runner ignores
MultiAgentConfig.min_agent_distance because env.add_agent is called with
position=None; update both the initial placement (around the env.add_agent call
at lines ~1603) and the per-run rebuild (around lines ~1652-1653) to honor
config.min_agent_distance: compute a concrete spawn position that is at least
config.min_agent_distance from all existing agents (using any existing helper
like find_spawn_position/select_spawn_position or by sampling/validating
positions against env.get_agent_positions()) and pass that position into
env.add_agent(position=spawn_pos, max_body_length=body_length); if env.add_agent
already supports a min_distance parameter, pass config.min_agent_distance
directly instead. Ensure you reference MultiAgentConfig.min_agent_distance,
env.add_agent, and the initial placement/rebuild code paths when making the
change.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and canât be posted inline due to platform limitations.
â ïļ Outside diff range comments (2)
packages/quantum-nematode/quantumnematode/env/env.py (2)
3022-3080:â ïļ Potential issue | ð Major
copy()does not preserve multi-agent state.The
copy()method only transfers the default agent's state via backward-compatible properties (lines 3054-3067). In multi-agent mode, all non-default agents would be lost. Ifcopy()is called during multi-agent simulation (e.g., for many-worlds branching), this could silently corrupt the environment.ð ïļ Suggested fix
new_env.body = self.body.copy() new_env.current_direction = self.current_direction new_env.foods = self.foods.copy() new_env.visited_cells = self.visited_cells.copy() + # Copy all agent states (multi-agent support) + new_env.agents = { + agent_id: AgentState( + agent_id=state.agent_id, + position=state.position, + body=state.body.copy(), + direction=state.direction, + hp=state.hp, + visited_cells=state.visited_cells.copy(), + wall_collision_occurred=state.wall_collision_occurred, + alive=state.alive, + steps_in_comfort_zone=state.steps_in_comfort_zone, + total_thermotaxis_steps=state.total_thermotaxis_steps, + steps_in_oxygen_comfort_zone=state.steps_in_oxygen_comfort_zone, + total_aerotaxis_steps=state.total_aerotaxis_steps, + ) + for agent_id, state in self.agents.items() + } # Copy RNG state for reproducibilityðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/env/env.py` around lines 3022 - 3080, The copy() implementation currently only copies the default agent's runtime state (body, current_direction, agent_pos, agent_hp, visited_cells) and thus drops any additional agents in multi-agent mode; update DynamicForagingEnvironment.copy to detect and deep-copy the multi-agent container (e.g., self.agents or whatever structure holds non-default agents) and for each agent clone its runtime fields (position, body, current_direction, hp, visited_cells/visited map entries, any per-agent thermotaxis/aerotaxis counters) into the new_env so multi-agent branches are preserved; keep the existing single-agent backward-compatible assignments (body, agent_pos, agent_hp, etc.) when only the default agent exists and ensure predators/rng are still copied as before.
1257-1286:â ïļ Potential issue | ðĄ MinorFood validation only checks default agent position.
_is_valid_food_position()at line 1274 usesself.agent_pos(default agent only). During multi-agent episodes, when food respawns after consumption (viaspawn_food()called fromconsume_food_for()), the validation does not check distances from non-default agents. Food could spawn on top of non-default agents. Consider checking all agent positions for proper multi-agent food placement.ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/env/env.py` around lines 1257 - 1286, The _is_valid_food_position function currently only measures distance to self.agent_pos, causing food to potentially spawn on non-default agents in multi-agent episodes; update _is_valid_food_position to iterate over all agent positions (e.g., self.agent_positions or the container used for agents in the environment) and check Euclidean distance against each agent's position (use the same exclusion radius self.foraging.agent_exclusion_radius) before validating food placement; also ensure spawn_food() / consume_food_for() continue to call this method so respawned food respects multi-agent exclusion rules and add a unit check to cover multi-agent scenarios if one exists.
âŧïļ Duplicate comments (1)
scripts/run_simulation.py (1)
1567-1572:â ïļ Potential issue | ðĄ MinorMissing qubit/classical brain validation for multi-agent.
The multi-agent path calls
validate_multi_agent_grid()but skipsvalidate_simulation_parameters(). This means a multi-agent config could specify non-defaultqubitsfor classical brain types without triggering the validation error that the single-agent path would catch.ð ïļ Suggested fix
# Validate grid size validate_multi_agent_grid(grid_size, num_agents) + # Validate brain/qubit compatibility for each agent + for ac in agent_configs: + validate_simulation_parameters( + maze_grid_size=grid_size, + brain_type=BrainType(ac.brain.name), + qubits=qubits, + ) logger.info(f"Multi-agent mode: {num_agents} agents, grid {grid_size}x{grid_size}")ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/run_simulation.py` around lines 1567 - 1572, The multi-agent path currently calls validate_multi_agent_grid(grid_size, num_agents) but never runs validate_simulation_parameters, allowing invalid combinations like non-default qubits with a classical brain to slip through; update the multi-agent startup to also call validate_simulation_parameters(multi_agent_config) (or pass the relevant fields such as multi_agent_config.brain_type and multi_agent_config.qubits) immediately alongside validate_multi_agent_grid so the same qubit/classical-brain checks applied in the single-agent flow are enforced for multi-agent runs.
ð§đ Nitpick comments (2)
packages/quantum-nematode/quantumnematode/env/env.py (1)
2070-2158: Consider extracting shared logic to reduce duplication.The
apply_temperature_effects_for()andapply_oxygen_effects_for()methods duplicate the zone-effect logic from the original single-agent methods (lines 2360-2407, 2529-2574). Consider extracting the core logic into a private helper that both variants call.ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/env/env.py` around lines 2070 - 2158, Both methods apply_temperature_effects_for and apply_oxygen_effects_for duplicate the same zone->reward/hp logic; extract that core logic into a private helper (e.g., _compute_and_apply_zone_effects or _apply_zone_effects) that takes the agent_state, zone, and the relevant config object (self.thermotaxis or self.aerotaxis) and returns (reward_delta, hp_damage); keep only zone lookup and per-agent counters in apply_temperature_effects_for/apply_oxygen_effects_for, call the new helper to compute rewards and damage, and let the helper handle mapping enums to comfort/danger/lethal rewards and applying hp clamping to agent_state.hp.scripts/run_simulation.py (1)
1619-1625:min_agent_distanceis best-effort; consider documenting this limitation.The
env.add_agent()call correctly passesmin_distance, but as noted in the environment code, this is best-effort Poisson sampling that may fall back to closer placement. If strict separation is required for certain experiments, users should be aware of this limitation.ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/run_simulation.py` around lines 1619 - 1625, The call to env.add_agent(...) passes min_distance (multi_agent_config.min_agent_distance) but the environment's placement is best-effort Poisson sampling and may place agents closer; update the code and/or docs to make this explicit: add a concise inline comment or user-facing warning near the env.add_agent invocation (or in the surrounding function) stating that min_distance is best-effort and may be violated, and update any public docstring or configuration help text (where multi_agent_config is defined) to document the limitation so users running experiments that require strict separation know to enforce it externally or add a post-placement check.
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/quantum-nematode/quantumnematode/env/env.py`:
- Around line 2219-2232: The fallback assignment of position (where position is
set via self.rng.integers) can place an agent on an occupied cell; change the
fallback in the block that handles Poisson sampling exhaustion so that after
generating a candidate (using self.rng.integers) you check against the occupied
set and retry until you find a free cell (or a small bounded number of retries),
then assign position; update the logger.warning to keep the same context
(agent_id, min_distance, MAX_POISSON_ATTEMPTS) but only log after a true
fallback decision, and reference the symbols position, self.rng.integers,
occupied, min_distance, MAX_POISSON_ATTEMPTS, and agent_id when making the fix.
---
Outside diff comments:
In `@packages/quantum-nematode/quantumnematode/env/env.py`:
- Around line 3022-3080: The copy() implementation currently only copies the
default agent's runtime state (body, current_direction, agent_pos, agent_hp,
visited_cells) and thus drops any additional agents in multi-agent mode; update
DynamicForagingEnvironment.copy to detect and deep-copy the multi-agent
container (e.g., self.agents or whatever structure holds non-default agents) and
for each agent clone its runtime fields (position, body, current_direction, hp,
visited_cells/visited map entries, any per-agent thermotaxis/aerotaxis counters)
into the new_env so multi-agent branches are preserved; keep the existing
single-agent backward-compatible assignments (body, agent_pos, agent_hp, etc.)
when only the default agent exists and ensure predators/rng are still copied as
before.
- Around line 1257-1286: The _is_valid_food_position function currently only
measures distance to self.agent_pos, causing food to potentially spawn on
non-default agents in multi-agent episodes; update _is_valid_food_position to
iterate over all agent positions (e.g., self.agent_positions or the container
used for agents in the environment) and check Euclidean distance against each
agent's position (use the same exclusion radius
self.foraging.agent_exclusion_radius) before validating food placement; also
ensure spawn_food() / consume_food_for() continue to call this method so
respawned food respects multi-agent exclusion rules and add a unit check to
cover multi-agent scenarios if one exists.
---
Duplicate comments:
In `@scripts/run_simulation.py`:
- Around line 1567-1572: The multi-agent path currently calls
validate_multi_agent_grid(grid_size, num_agents) but never runs
validate_simulation_parameters, allowing invalid combinations like non-default
qubits with a classical brain to slip through; update the multi-agent startup to
also call validate_simulation_parameters(multi_agent_config) (or pass the
relevant fields such as multi_agent_config.brain_type and
multi_agent_config.qubits) immediately alongside validate_multi_agent_grid so
the same qubit/classical-brain checks applied in the single-agent flow are
enforced for multi-agent runs.
---
Nitpick comments:
In `@packages/quantum-nematode/quantumnematode/env/env.py`:
- Around line 2070-2158: Both methods apply_temperature_effects_for and
apply_oxygen_effects_for duplicate the same zone->reward/hp logic; extract that
core logic into a private helper (e.g., _compute_and_apply_zone_effects or
_apply_zone_effects) that takes the agent_state, zone, and the relevant config
object (self.thermotaxis or self.aerotaxis) and returns (reward_delta,
hp_damage); keep only zone lookup and per-agent counters in
apply_temperature_effects_for/apply_oxygen_effects_for, call the new helper to
compute rewards and damage, and let the helper handle mapping enums to
comfort/danger/lethal rewards and applying hp clamping to agent_state.hp.
In `@scripts/run_simulation.py`:
- Around line 1619-1625: The call to env.add_agent(...) passes min_distance
(multi_agent_config.min_agent_distance) but the environment's placement is
best-effort Poisson sampling and may place agents closer; update the code and/or
docs to make this explicit: add a concise inline comment or user-facing warning
near the env.add_agent invocation (or in the surrounding function) stating that
min_distance is best-effort and may be violated, and update any public docstring
or configuration help text (where multi_agent_config is defined) to document the
limitation so users running experiments that require strict separation know to
enforce it externally or add a post-placement check.
ðŠ 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9c0ea847-2aca-4c7f-b022-d60a47b058cd
ð Files selected for processing (2)
packages/quantum-nematode/quantumnematode/env/env.pyscripts/run_simulation.py
add_agent fallback now retries against occupied set instead of placing blindly. _is_valid_food_position checks distance from ALL agents (not just default), so food respawn won't land on non-default agents. Document min_agent_distance as best-effort in config field comment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
ð§đ Nitpick comments (1)
packages/quantum-nematode/quantumnematode/utils/config_loader.py (1)
753-755: UseLiteraltypes forfood_competitionandtermination_policyto catch invalid values at config load time.Both fields accept arbitrary strings but the runtime (
MultiAgentOrchestrator) only accepts specific values. A typo liketermination_policy: "delete"will pass config loading but fail at orchestrator instantiation with a less helpful error.âŧïļ Suggested validation improvement
+from typing import Literal + +# At top of file or before MultiAgentConfig +FoodCompetitionPolicyStr = Literal["first_arrival", "random"] +TerminationPolicyStr = Literal["freeze", "remove", "end_all"] + class MultiAgentConfig(BaseModel): ... - food_competition: str = "first_arrival" + food_competition: FoodCompetitionPolicyStr = "first_arrival" social_detection_radius: int = 5 - termination_policy: str = "freeze" + termination_policy: TerminationPolicyStr = "freeze"ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/utils/config_loader.py` around lines 753 - 755, Replace the loose str annotations on the config fields with typing.Literal so invalid values are caught at load time: import Literal from typing and change the annotations for food_competition and termination_policy (the config fields named "food_competition" and "termination_policy" in this module) from str to Literal[...] listing the exact allowed string values used by MultiAgentOrchestrator (e.g., termination_policy: Literal["freeze","delete"] and food_competition: Literal["first_arrival", "closest", "random"]âor whatever exact set the orchestrator accepts); ensure the Literal options exactly match MultiAgentOrchestrator's accepted values so type checking and config validation fail early.
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/quantum-nematode/quantumnematode/env/env.py`:
- Around line 642-688: The render output currently only reads the
backward-compatible properties (agent_pos, body, current_direction) which point
at DEFAULT_AGENT_ID, so render()/render_full() omit non-default agents; update
render and render_full to iterate self.agents.values() (AgentState entries) and
render each agent's position, body, and direction rather than reading the
single-agent accessors; keep the existing agent_pos/body/current_direction
properties for backwards compatibility but stop using them inside
render/render_full and ensure rendering logic handles multiple AgentState
instances from self.agents and labels/draws them distinctly.
- Around line 2248-2257: copy() on DynamicForagingEnvironment currently
reconstructs only the default agent and drops any additional AgentState entries;
update DynamicForagingEnvironment.copy() to iterate over self.agents and rebuild
each AgentState into new_env.agents (instead of only building the default),
copying mutable fields (use state.body.copy(), state.visited_cells.copy()) and
preserving primitives/flags (state.position, state.direction, state.hp,
state.wall_collision_occurred, state.alive, steps_in_comfort_zone,
total_thermotaxis_steps, steps_in_oxygen_comfort_zone, total_aerotaxis_steps) so
the copied environment retains all agents and their full state.
- Around line 2161-2196: The add_agent method currently accepts a
caller-supplied position without validation; update add_agent to validate a
provided position before registering: check that position is within the
environment bounds (use the environment dimensions e.g. self.width/self.height
or existing bounds-checking helpers) and that the cell is not already occupied
(use the existing occupancy check logicâe.g. scan self.agents' positions or call
an existing self.is_occupied / is_cell_free helper); if the position is out of
bounds or occupied, raise ValueError with a clear message instead of inserting
the agent directly. Ensure this validation runs only when position is not None
and keep the existing random-placement/min_distance behavior unchanged.
---
Nitpick comments:
In `@packages/quantum-nematode/quantumnematode/utils/config_loader.py`:
- Around line 753-755: Replace the loose str annotations on the config fields
with typing.Literal so invalid values are caught at load time: import Literal
from typing and change the annotations for food_competition and
termination_policy (the config fields named "food_competition" and
"termination_policy" in this module) from str to Literal[...] listing the exact
allowed string values used by MultiAgentOrchestrator (e.g., termination_policy:
Literal["freeze","delete"] and food_competition: Literal["first_arrival",
"closest", "random"]âor whatever exact set the orchestrator accepts); ensure the
Literal options exactly match MultiAgentOrchestrator's accepted values so type
checking and config validation fail early.
ðŠ 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 51ac3085-fa38-4488-9ac8-cb9249cd1dde
ð Files selected for processing (2)
packages/quantum-nematode/quantumnematode/env/env.pypackages/quantum-nematode/quantumnematode/utils/config_loader.py
There was a problem hiding this comment.
ð§đ Nitpick comments (1)
packages/quantum-nematode/quantumnematode/agent/agent.py (1)
477-481: Consider exposing a public method for creating BrainParams with social context.The
_create_brain_paramsmethod is a private API (leading underscore), but based on the context snippets,MultiAgentSimulationcalls it directly from outside this class. While this works, it couples the orchestrator to an internal implementation detail.This is acceptable for now given the tight coupling between the agent and orchestrator in this multi-agent infrastructure, but consider adding a public wrapper method in a future iteration if the API surface stabilizes.
Also applies to: 607-609
ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/agent/agent.py` around lines 477 - 481, The code currently exposes the private method _create_brain_params which MultiAgentSimulation calls directly; add a public wrapper method create_brain_params(self, action: ActionData | None = None, nearby_agents_count: int | None = None) -> BrainParams that simply delegates to _create_brain_params (preserving behavior and docstring) so external orchestrators call the stable public API (create_brain_params) while keeping the original private implementation for internal use; update any external callers (e.g., MultiAgentSimulation) to use create_brain_params instead of _create_brain_params.
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/quantum-nematode/quantumnematode/agent/agent.py`:
- Around line 477-481: The code currently exposes the private method
_create_brain_params which MultiAgentSimulation calls directly; add a public
wrapper method create_brain_params(self, action: ActionData | None = None,
nearby_agents_count: int | None = None) -> BrainParams that simply delegates to
_create_brain_params (preserving behavior and docstring) so external
orchestrators call the stable public API (create_brain_params) while keeping the
original private implementation for internal use; update any external callers
(e.g., MultiAgentSimulation) to use create_brain_params instead of
_create_brain_params.
âđïļ Review info
âïļ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1bde0b73-0571-4641-9875-064c4ff9362a
ð Files selected for processing (7)
packages/quantum-nematode/quantumnematode/agent/agent.pypackages/quantum-nematode/quantumnematode/agent/multi_agent.pypackages/quantum-nematode/quantumnematode/brain/arch/_brain.pypackages/quantum-nematode/quantumnematode/brain/modules.pypackages/quantum-nematode/tests/quantumnematode_tests/agent/test_multi_agent.pypackages/quantum-nematode/tests/quantumnematode_tests/brain/test_social_module.pypackages/quantum-nematode/tests/quantumnematode_tests/env/test_env.py
â Files skipped from review due to trivial changes (2)
- packages/quantum-nematode/quantumnematode/brain/arch/_brain.py
- packages/quantum-nematode/tests/quantumnematode_tests/env/test_env.py
Mark SOCIAL_PROXIMITY module and nearby_agents_count as oracle sensing: exact agent count is biologically dishonest â real C. elegans detects conspecifics via ascaroside pheromone concentration gradients (ASK, ADL, ASI neurons), not by counting individuals. Pheromone-based temporal sensing planned for Phase 4 Deliverable 2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
âĶonfig types copy() now deep-copies all AgentState entries (not just default), preserving multi-agent state including per-agent body, visited_cells, HP, and comfort counters. add_agent() validates explicit positions: bounds check against grid_size, occupancy check against existing agent positions. Raises ValueError for out-of-bounds or occupied cells. MultiAgentConfig.food_competition and termination_policy use Literal types instead of bare str, catching invalid values at config parse time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@coderabbitai resume |
â Actions performedReviews resumed. |
|
@coderabbitai review |
â Actions performedReview triggered.
|
There was a problem hiding this comment.
âŧïļ Duplicate comments (1)
packages/quantum-nematode/quantumnematode/env/env.py (1)
2196-2205:â ïļ Potential issue | ðĄ MinorExplicit position validation doesn't check food/predator occupancy.
When
positionis explicitly provided, validation only checks against other agent positions (line 2202-2203). However, random placement (line 2209-2211) also excludes food and predator positions. This inconsistency could allow placing an agent directly on a food source or predator position.Consider whether this is intentional. If not, you may want to add food/predator position checks:
ðĄïļ Suggested fix to validate against food/predator positions
if position is not None: # Validate explicit position x, y = position if not (0 <= x < self.grid_size and 0 <= y < self.grid_size): msg = f"Position {position} out of bounds for grid size {self.grid_size}." raise ValueError(msg) occupied_positions = {a.position for a in self.agents.values()} + occupied_positions.update(self.foods) + if self.predator.enabled: + occupied_positions.update(p.position for p in self.predators) if position in occupied_positions: - msg = f"Position {position} already occupied by another agent." + msg = f"Position {position} is already occupied." raise ValueError(msg)ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/env/env.py` around lines 2196 - 2205, The explicit-position validation block currently only checks bounds and other agents (variables: position, self.grid_size, self.agents, occupied_positions); update it to also reject positions that collide with food and predators by computing the sets of food and predator positions (e.g., from self.food and self.predators or their position attributes) and checking membership before accepting the position so it matches the random-placement exclusion logic.
ð§đ Nitpick comments (3)
packages/quantum-nematode/quantumnematode/utils/config_loader.py (1)
911-932: Consolidate duplicated brain-resolution logic to reduce drift risk.
configure_brain_from_containerrepeats lookup/validation already present inconfigure_brain. Consider a shared helper so errors and behavior stay aligned.âŧïļ Suggested refactor
+def _configure_brain_by_name_and_config( + brain_name: str, + brain_config: BrainConfigType, +) -> BrainConfigType: + if brain_name not in BRAIN_CONFIG_MAP: + msg = f"Unknown brain type: {brain_name}." + raise ValueError(msg) + config_cls = BRAIN_CONFIG_MAP[brain_name] + return _resolve_brain_config(brain_config, config_cls, brain_name) + def configure_brain( config: SimulationConfig, ) -> BrainConfigType: @@ - brain_name = config.brain.name - - if brain_name not in BRAIN_CONFIG_MAP: - error_message = f"Unknown brain type: {config.brain.name}." - logger.error(error_message) - raise ValueError(error_message) - - config_cls = BRAIN_CONFIG_MAP[brain_name] - return _resolve_brain_config(config.brain.config, config_cls, brain_name) + return _configure_brain_by_name_and_config(config.brain.name, config.brain.config) @@ def configure_brain_from_container( brain_container: BrainContainerConfig, ) -> BrainConfigType: @@ - brain_name = brain_container.name - if brain_name not in BRAIN_CONFIG_MAP: - msg = f"Unknown brain type: {brain_name}." - raise ValueError(msg) - config_cls = BRAIN_CONFIG_MAP[brain_name] - return _resolve_brain_config(brain_container.config, config_cls, brain_name) + return _configure_brain_by_name_and_config(brain_container.name, brain_container.config)ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/utils/config_loader.py` around lines 911 - 932, configure_brain_from_container duplicates the brain-name lookup and validation logic in configure_brain; extract the shared lookup/validation into a small helper (e.g., get_brain_config_class or reuse an existing helper inside configure_brain) that takes a brain_name and returns the config class or raises the same ValueError, then have both configure_brain and configure_brain_from_container call that helper and continue to call _resolve_brain_config with the returned class; reference BRAIN_CONFIG_MAP, configure_brain_from_container, configure_brain, and _resolve_brain_config when making the change so behavior and error messages remain identical.packages/quantum-nematode/quantumnematode/env/env.py (2)
2241-2254: Consider expanding warning to cover full-grid scenario.The warning at lines 2247-2254 only triggers when
min_distance > 0, but the truly random fallback (lines 2242-2246) can also occur when the grid is nearly full regardless ofmin_distance. Consider logging a warning in both cases to alert callers that placement may overlap with food/predators.ð Suggested improvement
if position is None: # Last resort: truly random (grid is nearly full) position = ( int(self.rng.integers(0, self.grid_size)), int(self.rng.integers(0, self.grid_size)), ) - if min_distance > 0: logger.warning( - "Agent '%s' placed without min_distance=%d guarantee " - "(Poisson sampling exhausted %d attempts)", + "Agent '%s' placed at potentially occupied position %s " + "(exhausted %d attempts, min_distance=%d)", agent_id, + position, + MAX_POISSON_ATTEMPTS, min_distance, - MAX_POISSON_ATTEMPTS, )ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/env/env.py` around lines 2241 - 2254, The fallback that sets position via self.rng.integers(...) can occur even when min_distance == 0, but the existing logger.warning only runs inside the if min_distance > 0 block; update the placement logic so that whenever the Poisson sampler is exhausted and the code falls back to the truly random assignment (the position = (int(self.rng.integers(0, self.grid_size)), ...) branch), you emit a logger.warning referencing agent_id, MAX_POISSON_ATTEMPTS and that the grid was nearly full (and that placement may overlap food/predators), rather than only logging when min_distance > 0 â e.g. move or duplicate the logger.warning to run immediately after the random fallback so it executes regardless of min_distance.
3044-3108: Note:copy()overhead scales with agent count in hot paths.Based on the relevant code snippets,
env.copy()is called in hot paths during many-worlds visualization (runners.py:908,runners.py:960). The new implementation deep-copies allAgentStateentries including theirvisited_cellssets andbodylists. For the expected 2-10 agents, this overhead should be acceptable, but consider profiling if scaling to larger agent counts becomes necessary.ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/env/env.py` around lines 3044 - 3108, The copy() method on DynamicForagingEnvironment currently deep-copies each AgentState including visited_cells (set) and body (list), causing overhead in hot paths; change copy() to accept an optional parameter (e.g., share_agent_state: bool = False) and, when True, avoid deep-copying agent collections by either assigning AgentState objects directly or performing a shallow copy that reuses visited_cells and body references (modify the loop creating new_env.agents to check share_agent_state and either new_env.agents[aid] = state or create a lightweight AgentState that copies scalars but reuses state.visited_cells and state.body); keep existing behavior by default (share_agent_state=False) to preserve correctness and update callers in hot paths (runners.py visualization) to pass share_agent_state=True where safe.
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/quantum-nematode/quantumnematode/env/env.py`:
- Around line 2196-2205: The explicit-position validation block currently only
checks bounds and other agents (variables: position, self.grid_size,
self.agents, occupied_positions); update it to also reject positions that
collide with food and predators by computing the sets of food and predator
positions (e.g., from self.food and self.predators or their position attributes)
and checking membership before accepting the position so it matches the
random-placement exclusion logic.
---
Nitpick comments:
In `@packages/quantum-nematode/quantumnematode/env/env.py`:
- Around line 2241-2254: The fallback that sets position via
self.rng.integers(...) can occur even when min_distance == 0, but the existing
logger.warning only runs inside the if min_distance > 0 block; update the
placement logic so that whenever the Poisson sampler is exhausted and the code
falls back to the truly random assignment (the position =
(int(self.rng.integers(0, self.grid_size)), ...) branch), you emit a
logger.warning referencing agent_id, MAX_POISSON_ATTEMPTS and that the grid was
nearly full (and that placement may overlap food/predators), rather than only
logging when min_distance > 0 â e.g. move or duplicate the logger.warning to run
immediately after the random fallback so it executes regardless of min_distance.
- Around line 3044-3108: The copy() method on DynamicForagingEnvironment
currently deep-copies each AgentState including visited_cells (set) and body
(list), causing overhead in hot paths; change copy() to accept an optional
parameter (e.g., share_agent_state: bool = False) and, when True, avoid
deep-copying agent collections by either assigning AgentState objects directly
or performing a shallow copy that reuses visited_cells and body references
(modify the loop creating new_env.agents to check share_agent_state and either
new_env.agents[aid] = state or create a lightweight AgentState that copies
scalars but reuses state.visited_cells and state.body); keep existing behavior
by default (share_agent_state=False) to preserve correctness and update callers
in hot paths (runners.py visualization) to pass share_agent_state=True where
safe.
In `@packages/quantum-nematode/quantumnematode/utils/config_loader.py`:
- Around line 911-932: configure_brain_from_container duplicates the brain-name
lookup and validation logic in configure_brain; extract the shared
lookup/validation into a small helper (e.g., get_brain_config_class or reuse an
existing helper inside configure_brain) that takes a brain_name and returns the
config class or raises the same ValueError, then have both configure_brain and
configure_brain_from_container call that helper and continue to call
_resolve_brain_config with the returned class; reference BRAIN_CONFIG_MAP,
configure_brain_from_container, configure_brain, and _resolve_brain_config when
making the change so behavior and error messages remain identical.
âđïļ Review info
âïļ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ff895464-bb85-459f-bff6-5a09c2f7ce34
ð Files selected for processing (6)
packages/quantum-nematode/quantumnematode/brain/arch/_brain.pypackages/quantum-nematode/quantumnematode/brain/modules.pypackages/quantum-nematode/quantumnematode/env/env.pypackages/quantum-nematode/quantumnematode/utils/config_loader.pypackages/quantum-nematode/tests/quantumnematode_tests/agent/test_multi_agent.pypackages/quantum-nematode/tests/quantumnematode_tests/env/test_env.py
â Files skipped from review due to trivial changes (2)
- packages/quantum-nematode/tests/quantumnematode_tests/env/test_env.py
- packages/quantum-nematode/tests/quantumnematode_tests/agent/test_multi_agent.py
ð§ Files skipped from review as they are similar to previous changes (2)
- packages/quantum-nematode/quantumnematode/brain/arch/_brain.py
- packages/quantum-nematode/quantumnematode/brain/modules.py
âĶwarning add_agent() explicit-position validation now checks food and predator positions in addition to agents, matching the random-placement exclusion logic. Last-resort random placement (grid nearly full) now always warns regardless of min_distance setting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
â Actions performedReview triggered.
|
There was a problem hiding this comment.
ð§đ Nitpick comments (1)
packages/quantum-nematode/quantumnematode/env/env.py (1)
2455-2458: Consider adding*_forvariants for reset methods.
reset_thermotaxis()andreset_aerotaxis()only reset the default agent's counters. For multi-agent episode resets, the orchestrator would need to manually reset each agent's counters via directAgentStateaccess.If the orchestrator handles this externally (which it likely does given the design), this is fine. Otherwise, adding
reset_thermotaxis_for(agent_id)andreset_aerotaxis_for(agent_id)would provide a cleaner API.Also applies to: 2622-2625
ðĪ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/quantum-nematode/quantumnematode/env/env.py` around lines 2455 - 2458, Add per-agent reset variants so multi-agent orchestrators can reset counters without touching AgentState directly: implement reset_thermotaxis_for(agent_id) and reset_aerotaxis_for(agent_id) methods that locate the AgentState for the given agent_id and reset the same fields that reset_thermotaxis() / reset_aerotaxis() do (e.g., steps_in_comfort_zone, total_thermotaxis_steps, and corresponding aerotaxis counters), keep the existing no-arg methods as convenience wrappers that call the *_for methods for the default agent, and update the docstrings to reflect multi-agent usage.
ðĪ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/quantum-nematode/quantumnematode/env/env.py`:
- Around line 2455-2458: Add per-agent reset variants so multi-agent
orchestrators can reset counters without touching AgentState directly: implement
reset_thermotaxis_for(agent_id) and reset_aerotaxis_for(agent_id) methods that
locate the AgentState for the given agent_id and reset the same fields that
reset_thermotaxis() / reset_aerotaxis() do (e.g., steps_in_comfort_zone,
total_thermotaxis_steps, and corresponding aerotaxis counters), keep the
existing no-arg methods as convenience wrappers that call the *_for methods for
the default agent, and update the docstrings to reflect multi-agent usage.
âđïļ Review info
âïļ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: cb00a8e4-4a5e-4dea-a831-8b59f5aeba41
ð Files selected for processing (1)
packages/quantum-nematode/quantumnematode/env/env.py
Summary
AgentStatedataclass with backward-compatible property delegation â all existing single-agent code works unchangedMultiAgentSimulationorchestrator with configurable food competition (FIRST_ARRIVAL/RANDOM), termination policies (freeze/remove/end_all), and per-agent + aggregate metrics (Gini coefficient, competition events)Key changes
Environment (
env/env.py):AgentStatedataclass,agents: dict[str, AgentState], 20+*_for(agent_id)methods,add_agent(), multi-target predator pursuit (chase nearest alive agent)Orchestrator (
agent/multi_agent.py):MultiAgentSimulationwith synchronous step loop (perception â movement â food competition â predators â effects â learning),FoodCompetitionPolicy,MultiAgentEpisodeResultwith Gini coefficientBrain (
brain/arch/_brain.py,brain/modules.py):nearby_agents_countfield inBrainParams,SOCIAL_PROXIMITYsensory module (classical_dim=1)Config (
utils/config_loader.py):AgentConfig,MultiAgentConfigwith count/agents mutual exclusion validator,multi_agentfield onSimulationConfigScript (
scripts/run_simulation.py): Multi-agent branch with per-agent brain creation, shared env management, episode reset, per-agent weight persistence (final_{agent_id}.pt)Tests: 50 new tests (19 multi-agent orchestrator, 14 social proximity module, 17 env/AgentState)
Configs: 5 scenario YAMLs in
configs/scenarios/multi_agent_foraging/(2-agent, 5-agent, 10-agent, mixed brains, competition stress test)Functional verification (4 seeds x 5 scenarios)
Deferred to follow-up PR
agent_idcolumn (simulation_results.csv,multi_agent_summary.csv)Test plan
uv run pytest -m "not nightly"â 1984 passeduv run pre-commit run -aâ all 10 hooks pass (ruff, pyright, mdformat, markdownlint, etc.)ðĪ Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests
Chores