feat(phase0-predator-sensing): lock canonical predator-evasion sensor + reward (+14pp over legacy) - #188
Conversation
âĶnvestigation The corrected two-channel predator-sensing biology shipped in fix-predator-sensing-biology (Modelling caveat 6) learns substantially slower than legacy nociception_klinotaxis at matched compute budget: 500-episode preflight on MLPPPO shows last-25 success 60% legacy vs 16% new biology â a 44pp gap that persists despite 5x the smoke budget. Two candidate causes flagged in the predator-sensing change's design.md are tested here via three additive variants. Sensor variants (brain/modules.py): - `predator_mechanosensation_klinotaxis_sparse_fix`: same 3-dim shape as the canonical klinotaxis mechano channel, but the strength field falls back to `predator_distal_concentration` when ContactZone.NONE. Tests the "channel is mostly zero" hypothesis: canonical mechano emits 0.0 outside damage radius, so the brain has no signal to learn from on most steps; this variant substitutes the always-on distal concentration off-contact while preserving the in-contact intensity gradation + zone discrimination. - `predator_biology_klinotaxis`: composite single-channel module that collapses the canonical mechano+chemo split (3+3=6 dims) into one 4-dim block [intensity, zone_as_angle, distal_concentration, dconcentration_dt]. Tests the "6-dim represents 3 dims of info" hypothesis from the redundancy concern. Uses a SensoryModule subclass (PredatorBiologyCompositeModule) to override to_classical with a 4-dim emit since the base CoreFeatures->classical mapping supports 1/2/3 dims only. Mirrors STAMSensoryModule's pattern. STAM-dim inference (modules.py:_infer_stam_dim_from_modules): - sparse_fix variant added to the mechano triple â it counts as the same STAM channel as the canonical channels since the underlying env state is unchanged. - composite module counts as BOTH mechano and distal STAM channels (the env populates both regardless of brain-side encoding), with guards against double-counting when paired with the canonical channels. Reward variant (agent/agent.py + agent/reward_calculator.py): - `distal_chemo_contact_trigger` reward mode: continuous distal-chemo penalty (same env.get_predator_concentration field as gradient_proximity) PLUS the binary contact damage trigger (dist<=1 penalty inside is_in_danger). Drops the distance-scaled evasion term + flat fallback. Conceptually splits the predator reward into two biologically-motivated terms â continuous distal aversion + sharp contact pain â matching the dual-channel sensor biology (distal chemo + contact mechano) on the reward side. Tests (12 new): TestPredatorMechanosensationKlinotaxisSparseFix (4), TestPredatorBiologyKlinotaxisComposite (4), distal_chemo_contact_trigger 4 new tests under TestGradientOnlyRewardMode (4). All 79 tests in the two touched files pass; broader brain/agent/utils sweep (1986 tests) still clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
âĶomposite module
Pre-batch-eval review (and an empirical smoke run) caught a blocking
bug: the composite predator_biology_klinotaxis module ships in
brain/modules.py but agent/stam.py:resolve_active_channels was never
updated to recognise it. The function prefix-matches against
"predator_mechanosensation" and "predator_chemosensation"; the
composite name "predator_biology_klinotaxis" matches neither, so the
fallback legacy `predator` channel activates instead.
Concrete symptom: with composite-only configs, brain-side
_infer_stam_dim_from_modules counted both new channels (dim 9) but
env-side resolve_active_channels returned 2 channels (food + legacy
predator, dim 7). The brain's first Linear layer is sized for 18 input
dims but the runtime feeds 16, producing a shape-mismatch crash on the
first forward pass:
RuntimeError: mat1 and mat2 shapes cannot be multiplied (1x16 and 18x64)
Fix: explicit recognition of "predator_biology_klinotaxis" in
resolve_active_channels â both has_new_mechano and has_new_distal flip
true when the composite is in the module list. The composite pulls
from BOTH env-side channels (intensity from mechano contact, distal
concentration from chemo) so activating both is correct; the legacy
fallback stays inactive.
Regression test (5 new): TestSTAMChannelResolutionAgreesWithModuleInference
parametrizes over 4 module-list configurations (canonical two-channel,
sparse-fix variant, composite-alone, legacy nociception) and asserts
stam_dim_from_env == _infer_stam_dim_from_modules for each â the
exact invariant that broke silently. Plus an explicit
test_composite_activates_both_new_predator_channels that asserts the
composite-alone path activates predator_mechano AND predator_distal
without falling back to legacy.
All three Phase 0 ablation configs now smoke-launch cleanly. Broader
brain/agent test sweep (1647 tests) still passes.
Also fix a misleading inline YAML comment in the B0.5 reward-ablation
config that said "CHANGED from default gradient_proximity" â the
correct phrasing is "CHANGED from 'default' mode (RewardConfig
default)" since the baseline's reward_mode is "default", not
"gradient_proximity".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
âĶen distal-chemo klinotaxis mode Bio-fidelity audit during Phase 0 caught a silent bug shipped at T3: the predator_lateral_gradient field â the head-sweep right-minus-left predator concentration delta that the chemo channel's klinotaxis angle feature depends on â was gated solely on the LEGACY nociception_mode == KLINOTAXIS. New biology-driven predator-evasion configs set predator_distal_mode: klinotaxis but leave nociception_mode at its ORACLE default (the legacy field is irrelevant for new-module configs). Consequence: the new predator_chemosensation_klinotaxis module read params.predator_lateral_gradient = None and emitted angle = tanh(0.0) = 0.0 on every step. The chemo channel was silently emitting only 2 of its 3 designed klinotaxis features â the brain had NO head-sweep directional information about predators despite the channel claiming klinotaxis mode. Likely the largest contributor to the 44pp convergence gap observed between legacy nociception_klinotaxis (60% last-25 success at n=4 seeds à 500 ep) and new biology (16-23% across A2/B0.3/B0.4/B0.5 ablation variants) in the Phase 0 Step B sweep: directional info is load-bearing for predator evasion (legacy A1 evades 83% of encounters per encounter; new biology 76-78%), and per-encounter probability compounds across the ~4 encounters per episode. Fix: extend the gate to fire under EITHER nociception_mode == KLINOTAXIS (legacy path, preserved for the 22 archived nociception_klinotaxis configs) OR predator_distal_mode == KLINOTAXIS (new biology path). Both gates feed the same env-side get_predator_concentration() field, so the underlying numeric value is identical â the dual gate just removes the silent silencing of the new path. Regression test (3 new) TestPredatorLateralGradientPopulation: - Legacy path still populates the field (no regression for A1 configs). - New biology path now populates the field (the bug-fix scenario). - Both gates off leaves the field None (default behaviour preserved). Broader agent + brain test sweep (1650 tests) clean. Bug 2 from the audit (`get_predator_sulfolipid_concentration` is an alias of `get_predator_concentration` rather than a literature- calibrated distinct signal) is the explicit T6/T7 deferral documented in `fix-predator-sensing-biology` archived design.md Decision T3.5 and is NOT addressed here. Re-evaluating Step B with Bug 1 fixed should tell us whether Bug 2 also needs to land for Phase 4 viability or can wait per the original design boundary. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
âĶstigation Phase 0 of weight-search-architecture-ranking ran 40 canonical-budget runs (n=4 seeds à 500 episodes à 6 variants on MLPPPO small + klinotaxis) to settle the convergence gap from fix-predator-sensing-biology Modelling caveat 6. Result: B0.5 (canonical two-channel sensors + new distal_chemo_contact_trigger reward) wins at 81.0% Âą 5.0 vs legacy 67.0% Âą 7.6 (+14pp), with the lowest death rate (18%) and tightest variance across all variants. The orthogonal stack B0.6 (sparse_fix + new reward) does not compound, so the canonical stays with the simpler biology-default mechano channel. Locked in writing: - design.md gains "Phase 0 canonical-variant selection" section documenting both bugs uncovered (composite STAM recognition, predator lateral_gradient gating), the full ranking, and carry-forward implications for T6/T7. - spec.md predator-sensing-biology requirement gains a "Canonical variant selected (Phase 0 outcome)" scenario naming the canonical sensor pair and reward mode. - Forensics promoted to docs/experiments/logbooks/supporting/ 025-weight-search-architecture-ranking/phase-0/ (README narrative + per-seed results CSV + summary stats CSV). - tasks.md marks 0.1-0.8 complete. No code changes â Bug 1 fix (commit 65a5b51) and STAM composite fix (commit c25588a) already merged earlier on this branch.
âĶmplementation Phase 0 diverged from the originally-planned sub-task structure in two ways: (i) the planned "B0.5 2x2 cross of best-sensor x reward" expanded into B0.5 (canonical sensors + new reward, the winner) + B0.6 (sparse_fix sensor + new reward, orthogonal-stack extended validation) + the post-Bug-1-fix re-runs of A2/B0.3/B0.5 supplying the remaining 2x2 cells; (ii) two implementation bugs were uncovered + fixed mid-investigation (composite STAM channel recognition; predator_lateral_gradient silent silencing under new-biology configs) and shipped as Phase 0 deliverables rather than being deferred. Reconciliation: - tasks.md: add an Implementation note documenting the two divergences, amend Task 0.6 framing from "2x2 cross" to "sensor x reward joint mapping" with the realised breakdown, note Task 0.4's conditional MODIFIED delta did NOT trigger (composite did not win), add Task 0.9 for the two bug fixes. - design.md Open Questions Q1: mark Settled with reference to the canonical-variant-selection section. - design.md Decision 8 compute pre-estimate: refresh Phase 0 budget from "~24 runs" pre-estimate to "~40 runs actual" with breakdown + rationale for the overshoot. - proposal.md Phase 0 narrative: add explicit bullet for shipping bug fixes uncovered during investigation. - proposal.md Impact section: enumerate the actual code shipped (3 opt-in sensor/reward variants + 2 bug fixes with test counts + commit hashes). Also strip planning-label leakage from implementation code per project memory feedback (no Tranche/Phase/Decision refs in code): - agent.py:746 Bug-1-fix comment: drop "Until T3" historical phrasing; describe the dual-gate technical contract directly. - test_stam.py:357 parametrize case label: drop "(frozen pre-T3 path)"; reword as "(frozen single-channel predator path)". No code-behaviour change; comments + planning artefacts only. Targeted pre-commit clean; 61 tests pass on the touched test files.
Five review nits from the pre-push spec + code review pass:
- proposal.md: replace stale `0XX-weight-search-architecture-ranking`
logbook-number placeholders with the locked `025-` (design.md
Decision 8 had already committed to 025; proposal.md was the only
artefact still using the placeholder).
- proposal.md: fix `Impact > New configs` claim that "All [configs]
[live] under configs/scenarios/" â the GA combined config lives under
configs/evolution/ per design.md Decision 8's deliberate launcher
split (GA needs an `evolution:` block + scripts/run_evolution.py).
Also call out tmp/ for Phase 0 ablation configs.
- spec.md "Canonical variant selected" scenario: rewrite the 40-run
arithmetic from the confusing "40 runs (n=4 à 500ep à 6 variants)"
(which multiplies to 24, not 40) to the actual breakdown â 40 runs
across Step A + Step B pre-fix + Step B re-run + extended validation,
with n=4 Ã 500ep per variant.
- design.md Phase 0 risk row: mark RESOLVED with strikethrough â the
convergence-gap-fails-to-close risk did not fire (Phase 0 overshot
legacy by +14pp, gap was a silent-silencing bug not a substrate
finding). Mitigation pivots (raise budget; drop predator from change)
were not needed.
- test_stam.py: add two parametrise cases to
`test_env_and_module_inference_agree` exercising the
composite-plus-canonical co-presence dedupe in
`brain/modules.py:_infer_stam_dim_from_modules`:
- composite + canonical mechano only (distal dedupe guard exercised)
- composite + both canonical channels (both guards exercised
simultaneously; without them brain builds for 5 STAM channels vs
env-resolved 3)
Tests pass (36 in file; was 34 before).
`openspec validate weight-search-architecture-ranking --strict` clean;
targeted pre-commit clean.
|
No actionable comments were generated in the recent review. ð âđïļ Recent review infoâïļ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ð Files selected for processing (7)
ð§ Files skipped from review as they are similar to previous changes (5)
ð WalkthroughWalkthroughPhase 0 completes a sensorÃreward ablation: adds a ChangesPhase 0 predator-sensing biology architecture ranking
Estimated code review effortðŊ 3 (Moderate) | âąïļ ~25 minutes Possibly related PRs
Poem
ðĨ Pre-merge checks | â 5â Passed checks (5 passed)
âïļ Tip: You can configure your own custom pre-merge checks in the settings. âĻ Finishing Touchesð Generate docstrings
ð§Š Generate unit tests (beta)
Comment |
Codecov Reportâ All modified and coverable lines are covered by tests. ðĒ Thoughts on this report? Let us know! |
âĶard in pursuit configs
Two related cleanups per user feedback ("we should not make references to
any files in tmp since those are ephemeral"):
1. Promote the five Phase 0 evaluation configs from tmp/ into
docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/
as reproducibility artefacts. Sanitise each header to point at the
supporting/ path instead of "lives under tmp/", explain why each
variant was NOT promoted into configs/scenarios/ (composite empirically
inferior; sparse_fix beaten by canonical; orthogonal stack within
noise of simpler canonical; legacy superseded by two-channel biology).
Update supporting/025/phase-0/README.md: replace tmp/ path table with
./configs/* relative links and drop the scratchpad reference (the
scratchpad lives under tmp/ as working forensics per Decision 6 but
the published supporting/ README MUST NOT cite tmp/).
2. Ship the Phase 0 canonical reward in the two canonical pursuit configs
that previously inherited the default reward:
- configs/scenarios/pursuit/mlpppo_small_predator_biology_klinotaxis.yml
- configs/scenarios/pursuit/lstmppo_small_predator_biology_klinotaxis.yml
Both gain `reward_mode: distal_chemo_contact_trigger` (Phase 0 winner
at +14pp over legacy on MLPPPO). Header comments updated with the
empirical justification + caveat that LSTMPPO has not been validated
at canonical Phase 4 budget yet, so per-architecture re-evaluation
may revise. Both configs verified to load + parse the new reward
mode via load_simulation_config.
Also update proposal.md "New configs" line to reflect that Phase 0
ablation configs now live under supporting/ (not tmp/).
openspec validate strict clean; targeted pre-commit clean.
There was a problem hiding this comment.
Actionable comments posted: 1
ð§đ Nitpick comments (4)
openspec/changes/weight-search-architecture-ranking/design.md (1)
174-177: ⥠Quick winKeep
design.mdnon-normative; move SHALL language to spec-only wording.Line 174 and Line 190 use normative SHALL/MUST-style requirement language inside
design.md. Please rephrase these to rationale/decision wording (e.g., âis carried forward as the selected defaultâ), and keep strict normative requirements inspec.md.Suggested wording adjustment
-**Phase 4 C-curriculum predator-evasion cells SHALL use:** +**Phase 4 C-curriculum predator-evasion cells use the selected canonical setup:** -- **T7 (L2 re-run on upgraded substrate)** SHALL consume the same canonical ... +- **T7 (L2 re-run on upgraded substrate)** is planned to consume the same canonical ...Based on learnings: Design.md files under openspec/changes/ should document architectural/design rationale and should not use SHALL/MUST language; normative requirements belong in spec.md files.
Also applies to: 190-190
ðĪ 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 `@openspec/changes/weight-search-architecture-ranking/design.md` around lines 174 - 177, The design document uses normative SHALL/MUST phrasing (e.g., the block starting "Phase 4 C-curriculum predator-evasion cells SHALL use:" and the similar line at 190) which must be converted to non-normative rationale language and any strict requirements moved to the spec; update those sentences to neutral wording such as "is carried forward as the selected default: Sensors ...; Reward ..." or "we select X as the default" and remove/relocate any MUST/SHALL phrasing to the corresponding spec file, ensuring the listed identifiers (predator_mechanosensation_klinotaxis, predator_chemosensation_klinotaxis, reward_mode: distal_chemo_contact_trigger, env.get_predator_concentration) remain as decision/rationale examples rather than normative mandates.docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_klinotaxis.yml (1)
53-67: ⥠Quick winPin
reward_modeexplicitly for reproducibility.This config currently depends on an implicit default. For logbook-grade reproducibility, set
reward_mode: defaultexplicitly so future default changes donât alter B0.3 behavior.Proposed diff
reward: + reward_mode: default reward_goal: 2.0 reward_distance_scale: 0.5 reward_exploration: 0.05ðĪ 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 `@docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_klinotaxis.yml` around lines 53 - 67, The config relies on an implicit default for reward_mode; explicitly add the key "reward_mode: default" inside the reward block to pin behavior for reproducibility (i.e., insert reward_mode: default alongside the other keys under the reward mapping in this YAML so future default changes wonât affect this experiment).packages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.py (1)
458-471: ⥠Quick winConvert the new docstrings to NumPy style for guideline compliance.
The added docstrings are clear, but theyâre not in NumPy docstring format required by this repo.
Proposed update pattern
def _create_predator_agent( self, sensing_config: "SensingConfig", ) -> QuantumNematodeAgent: + """Create a predator-enabled agent for lateral-gradient regression tests. + + Parameters + ---------- + sensing_config : SensingConfig + Sensing configuration used to construct the agent. + + Returns + ------- + QuantumNematodeAgent + Configured agent bound to a predator-enabled dynamic environment. + """ from quantumnematode.brain.actions import Action from quantumnematode.env.env import PredatorParamsAs per coding guidelines:
Use NumPy-style docstrings for documentation.Also applies to: 507-508, 517-523, 537-537
ðĪ 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 `@packages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.py` around lines 458 - 471, Update the multi-line test docstring that begins "Regression: dual-gate test for predator_lateral_gradient population." to NumPy style: start with a one-line summary, blank line, then an extended description paragraph(s) explaining the behavior and the pre-fix bug, and (if applicable) add "Notes" or "See Also" sections rather than free-form text; keep it in NumPy conventions (section headers underlined with dashes, e.g., "Notes\n-----"). Apply the same conversion to the other nearby docstrings referenced (the ones around the subsequent blocks), ensuring they follow the same one-line summary + extended description + optional NumPy sections format.packages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.py (1)
307-314: ⥠Quick winAdd an explicit return type for
_make_predator_env.
_make_predator_envis missing a return annotation, which breaks the repoâs typing rule for comprehensive annotations.Suggested patch
-def _make_predator_env(*, thermotaxis_enabled: bool = False, aerotaxis_enabled: bool = False): +def _make_predator_env( + *, + thermotaxis_enabled: bool = False, + aerotaxis_enabled: bool = False, +) -> Mock:As per coding guidelines: "Comprehensive type annotations are required in all code".
ðĪ 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 `@packages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.py` around lines 307 - 314, The helper _make_predator_env lacks a return type annotation; add an explicit return type (e.g. change its signature to include "-> Mock") so it returns a typing-annotated Mock object, and ensure the unittest.mock.Mock symbol is imported/available in that test module; update the function definition _make_predator_env to include the return annotation only.
ðĪ 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 `@packages/quantum-nematode/quantumnematode/agent/agent.py`:
- Around line 749-752: The pre-gate that computes lateral-offsets fails to
include predator_distal_mode in the any_klinotaxis check, so when
sensing.predator_distal_mode == SensingMode.KLINOTAXIS left_pos/right_pos remain
agent_pos and predator_lateral_gradient collapses to 0; update the
any_klinotaxis condition (the boolean used earlier) to include
sensing.predator_distal_mode (same SensingMode.KLINOTAXIS check used at the
later gate) so the lateral-offset branch runs for predator_distal_mode and
left_pos/right_pos are computed instead of staying at agent_pos.
---
Nitpick comments:
In
`@docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_klinotaxis.yml`:
- Around line 53-67: The config relies on an implicit default for reward_mode;
explicitly add the key "reward_mode: default" inside the reward block to pin
behavior for reproducibility (i.e., insert reward_mode: default alongside the
other keys under the reward mapping in this YAML so future default changes wonât
affect this experiment).
In `@openspec/changes/weight-search-architecture-ranking/design.md`:
- Around line 174-177: The design document uses normative SHALL/MUST phrasing
(e.g., the block starting "Phase 4 C-curriculum predator-evasion cells SHALL
use:" and the similar line at 190) which must be converted to non-normative
rationale language and any strict requirements moved to the spec; update those
sentences to neutral wording such as "is carried forward as the selected
default: Sensors ...; Reward ..." or "we select X as the default" and
remove/relocate any MUST/SHALL phrasing to the corresponding spec file, ensuring
the listed identifiers (predator_mechanosensation_klinotaxis,
predator_chemosensation_klinotaxis, reward_mode: distal_chemo_contact_trigger,
env.get_predator_concentration) remain as decision/rationale examples rather
than normative mandates.
In `@packages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.py`:
- Around line 458-471: Update the multi-line test docstring that begins
"Regression: dual-gate test for predator_lateral_gradient population." to NumPy
style: start with a one-line summary, blank line, then an extended description
paragraph(s) explaining the behavior and the pre-fix bug, and (if applicable)
add "Notes" or "See Also" sections rather than free-form text; keep it in NumPy
conventions (section headers underlined with dashes, e.g., "Notes\n-----").
Apply the same conversion to the other nearby docstrings referenced (the ones
around the subsequent blocks), ensuring they follow the same one-line summary +
extended description + optional NumPy sections format.
In `@packages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.py`:
- Around line 307-314: The helper _make_predator_env lacks a return type
annotation; add an explicit return type (e.g. change its signature to include
"-> Mock") so it returns a typing-annotated Mock object, and ensure the
unittest.mock.Mock symbol is imported/available in that test module; update the
function definition _make_predator_env to include the return annotation only.
ðŠ 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: 00b6c908-b514-44a2-b0f1-43a84328f647
â Files ignored due to path filters (2)
docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/per-seed-results.csvis excluded by!**/*.csvdocs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/summary-stats.csvis excluded by!**/*.csv
ð Files selected for processing (20)
configs/scenarios/pursuit/lstmppo_small_predator_biology_klinotaxis.ymlconfigs/scenarios/pursuit/mlpppo_small_predator_biology_klinotaxis.ymldocs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/README.mddocs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_legacy_nociception_klinotaxis_control.ymldocs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_biology_klinotaxis_distal_chemo_contact_trigger.ymldocs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_composite_klinotaxis.ymldocs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_distal_chemo_contact_trigger_klinotaxis.ymldocs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_klinotaxis.ymlopenspec/changes/weight-search-architecture-ranking/design.mdopenspec/changes/weight-search-architecture-ranking/proposal.mdopenspec/changes/weight-search-architecture-ranking/specs/predator-sensing-biology/spec.mdopenspec/changes/weight-search-architecture-ranking/tasks.mdpackages/quantum-nematode/quantumnematode/agent/agent.pypackages/quantum-nematode/quantumnematode/agent/reward_calculator.pypackages/quantum-nematode/quantumnematode/agent/stam.pypackages/quantum-nematode/quantumnematode/brain/modules.pypackages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.pypackages/quantum-nematode/tests/quantumnematode_tests/agent/test_reward_calculator.pypackages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.pypackages/quantum-nematode/tests/quantumnematode_tests/brain/test_predator_modules.py
âĶR-review nits
PR-review findings, audited against current code:
**Finding 1 (real bug, latent â fix)**
`_compute_temporal_data`'s `any_klinotaxis` outer-guard at agent.py:710
omitted `sensing.predator_distal_mode` from the SensingMode.KLINOTAXIS
tuple. When a config sets ONLY `predator_distal_mode: klinotaxis` (no
other klinotaxis knob), `_compute_lateral_offsets` is skipped, leaving
`left_pos == right_pos == agent_pos`. The inner predator gate fired
correctly (Bug 1 fix preserved) but sampled the same position twice â
silent collapse to `predator_lateral_gradient == 0.0`. Production
configs at configs/scenarios/pursuit/{mlpppo,lstmppo}_small_predator_biology_klinotaxis.yml
mask this by also setting `chemotaxis_mode: klinotaxis`, but a
chemo-only-klinotaxis predator config would have hit the silent zero.
Fix: include `sensing.predator_distal_mode` in the any_klinotaxis tuple.
Regression test strengthened: `test_new_biology_distal_klinotaxis_populates_lateral_gradient`
now (a) sets `chemotaxis_mode=ORACLE` to isolate the bug surface, and
(b) stubs `env.get_predator_concentration` to a position-dependent
value so the assertion `predator_lateral_gradient != 0.0` actually
fires when head-sweep offsets are not computed. Verified: reverting
the agent.py fix makes this test fail; restoring it makes it pass.
**Nitpick 1 (apply): pin reward_mode in supporting reproducibility configs**
Three Phase 0 supporting configs (A1 legacy, B0.3 sparse_fix, B0.4
composite) relied on RewardConfig's `default` Literal as an implicit
default. Added `reward_mode: default` explicitly to all three so the
frozen experiment forms are pinned against future Literal default
changes.
**Nitpick 2 (apply): soften design.md SHALL â rationale**
design.md § "Canonical lock" + § "Carry-forward implications" used
normative SHALL/SHALL phrasing that duplicated the normative claim
already carried by spec.md's "Canonical variant selected (Phase 0
outcome)" scenario (which IS the spec-level lock). Reworded to neutral
"is carried forward as the selected default" + "is expected to" +
pointer to the spec scenario where the normative SHALL actually lives.
Per OpenSpec convention: design.md is rationale, spec.md is normative.
**Nitpick 3 (skip): NumPy docstring sections in test_agent.py**
Project pydocstyle convention IS numpy (pyproject.toml), but existing
test files (test_satiety.py, test_reward_calculator.py, etc., all on
main) use the same free-form one-line-summary + extended-description
style that ruff accepts under the per-file-ignores for test files.
Forcing NumPy `Notes` / `See Also` sections here would diverge from
the file's existing style and bring no actionable benefit. Skipped.
**Nitpick 4 (apply): add Mock return annotation to _make_predator_env**
`_make_predator_env` helper in test_stam.py lacked a return type. Mock
is already imported. Added `-> Mock` annotation.
openspec validate weight-search-architecture-ranking --strict clean;
63 tests pass on test_agent.py + test_stam.py; targeted pre-commit clean.
|
Actionable comments posted: 0 |
Summary
weight-search-architecture-ranking: settle the predator-sensing convergence-rate gap deferred fromfix-predator-sensing-biologyModelling caveat 6 (new biology learned 44pp slower than legacy at canonical Phase 4 budget).agent.py:_compute_temporal_datagatedpredator_lateral_gradientpopulation on the legacynociception_mode == KLINOTAXISonly; new-biology configs usepredator_distal_mode: klinotaxisand the gradient field was permanentlyNone, collapsing the chemo channel's directionalanglefeature to 0.0. Dual-gate fix (commit65a5b517) closed the entire 44pp gap.predator_mechanosensation_klinotaxis+predator_chemosensation_klinotaxissensors +reward_mode: distal_chemo_contact_triggerreward â beats legacy by +14pp (81.0% Âą 5.0 vs 67.0% Âą 7.6 last-25 mean success), lowest variance, lowest death rate (18% vs 32%) of all 6 evaluated variants at n=4 Ã 500ep on MLPPPO small + klinotaxis.This PR ships Phase 0 only. Phases 1-7 of the change (NEAT integration verification, ConnectomePPO predator-gains projection, combined-behaviour configs, the 4-architecture comparison sweep, BH-FDR analysis, logbook publication) are intentionally NOT in scope â they land in follow-up PRs.
What ships
Code (additive â opt-in only, no canonical paths touched):
predator_mechanosensation_klinotaxis_sparse_fix+predator_biology_klinotaxis(composite, 4-dim). Ablation variants only; canonical Phase 4 cells use the existing two-channel biology.reward_mode: distal_chemo_contact_triggerâ continuous distal-chemo penalty + binary contact damage trigger. This becomes the Phase 4 canonical.agent/stam.pycomposite STAM-channel recognition (commitc25588a1) â composite module fell through to legacypredatorfallback channel, causing brain/env STAM-dim disagreement + first-forward-pass shape mismatch crash.agent/agent.pypredator_lateral_gradientdual-gate (commit65a5b517) â the load-bearing fix that closed the convergence gap.Tests:
TestPredatorLateralGradientPopulation).OpenSpec:
Logbook supporting/:
docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/with README narrative + per-seed-results.csv + summary-stats.csv. Logbook 025 itself is published in Phase 5 (separate PR).Final Phase 0 ranking (n=4 Ã 500ep, post-Bug-1-fix)
Orthogonal-stack (sparse_fix + new reward) does not compound â once the new reward provides a continuous distal-chemo penalty, the sparse_fix mechano's always-on distal fallback becomes redundant. Composite is structurally inferior (post-Bug-1 score byte-identical to pre-fix because it drops
lateral_gradientby design); the redundancy hypothesis it tested is empirically refuted.Test plan
uv run pytest -m "not nightly" packages/quantum-nematode/tests/quantumnematode_tests/agent/ packages/quantum-nematode/tests/quantumnematode_tests/brain/â all green (1626 passed).openspec validate weight-search-architecture-ranking --strictâ clean.pre-commit run -aâ all hooks pass.git blameâ all flagged refs trace to already-merged commits).>100KBartefacts; no home-path leaks in committed content.docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/(per-seed CSV + summary CSV).ðĪ Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests