Skip to content

feat(phase0-predator-sensing): lock canonical predator-evasion sensor + reward (+14pp over legacy) - #188

Merged
chrisjz merged 8 commits into
mainfrom
feat/phase0-predator-sensing-investigation
May 28, 2026
Merged

feat(phase0-predator-sensing): lock canonical predator-evasion sensor + reward (+14pp over legacy)#188
chrisjz merged 8 commits into
mainfrom
feat/phase0-predator-sensing-investigation

Conversation

@chrisjz

@chrisjz chrisjz commented May 28, 2026

Copy link
Copy Markdown
Member

Summary

  • Investigation-led Phase 0 of weight-search-architecture-ranking: settle the predator-sensing convergence-rate gap deferred from fix-predator-sensing-biology Modelling caveat 6 (new biology learned 44pp slower than legacy at canonical Phase 4 budget).
  • Root cause + fix: a silent-silencing bug — agent.py:_compute_temporal_data gated predator_lateral_gradient population on the legacy nociception_mode == KLINOTAXIS only; new-biology configs use predator_distal_mode: klinotaxis and the gradient field was permanently None, collapsing the chemo channel's directional angle feature to 0.0. Dual-gate fix (commit 65a5b517) closed the entire 44pp gap.
  • Canonical locked for Phase 4: predator_mechanosensation_klinotaxis + predator_chemosensation_klinotaxis sensors + reward_mode: distal_chemo_contact_trigger reward — 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):

  • 2 sensor modules: predator_mechanosensation_klinotaxis_sparse_fix + predator_biology_klinotaxis (composite, 4-dim). Ablation variants only; canonical Phase 4 cells use the existing two-channel biology.
  • 1 reward mode: reward_mode: distal_chemo_contact_trigger — continuous distal-chemo penalty + binary contact damage trigger. This becomes the Phase 4 canonical.
  • 2 bug fixes:
    • agent/stam.py composite STAM-channel recognition (commit c25588a1) — composite module fell through to legacy predator fallback channel, causing brain/env STAM-dim disagreement + first-forward-pass shape mismatch crash.
    • agent/agent.py predator_lateral_gradient dual-gate (commit 65a5b517) — the load-bearing fix that closed the convergence gap.

Tests:

  • 16 new tests for the new modules/reward (test_predator_modules.py + test_reward_calculator.py).
  • 8 regression tests for the two bug fixes (test_stam.py +5; test_agent.py +3 under TestPredatorLateralGradientPopulation).
  • 2 additional STAM-dim parametrise cases for composite + canonical co-presence dedupe.

OpenSpec:

  • design.md gains § "Phase 0 canonical-variant selection" with full ranking + bug-fix story + carry-forward implications.
  • spec.md predator-sensing-biology gains scenario "Canonical variant selected (Phase 0 outcome)" naming the canonical sensor pair + reward mode.
  • tasks.md 0.1–0.9 checked; 1.x–7.x deliberately unchecked.

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)

Rank Variant last-25 success death%
🏆 1 canonical sensors + new reward (locked) 81.0% Âą 5.0 18.0%
2 sparse_fix sensor + new reward (orthogonal stack) 78.0% Âą 6.9 22.0%
3 sparse_fix sensor + default reward 73.0% Âą 6.0 27.0%
4 legacy nociception_klinotaxis (reference) 67.0% Âą 7.6 32.0%
5 canonical new biology + default reward 65.0% Âą 16.1 32.0%
6 composite single-channel 16.0% Âą 7.3 83.0%

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_gradient by 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.
  • No planning-label leakage in branch-authored implementation code (audited via git blame — all flagged refs trace to already-merged commits).
  • No >100KB artefacts; no home-path leaks in committed content.
  • Reviewers: please sanity-check the Phase 0 canonical-variant claims against 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

    • Composite predator-sensing module (dual-channel encoding), sparse-signal mechanosensation, and a new "distal_chemo_contact_trigger" reward mode.
  • Bug Fixes

    • Fixed predator gradient gating and STAM channel-dimension inference; addressed two Phase‑0 sensor/gradient bugs that affected convergence.
  • Documentation

    • Added Phase‑0 logbook, design/spec updates, and multiple experiment configs documenting canonical sensor×reward choices.
  • Tests

    • Added regression tests covering lateral-gradient population, new reward mode behavior, STAM channel resolution, and predator sensor modules.

Review Change Stack

chrisjz and others added 6 commits May 27, 2026 21:56
â€Ķ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.
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

â„đïļ Recent review info
⚙ïļ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1f963910-8c14-4325-b297-35b25638ed90

ðŸ“Ĩ Commits

Reviewing files that changed from the base of the PR and between b855526 and fa224af.

📒 Files selected for processing (7)
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_legacy_nociception_klinotaxis_control.yml
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_composite_klinotaxis.yml
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_klinotaxis.yml
  • openspec/changes/weight-search-architecture-ranking/design.md
  • packages/quantum-nematode/quantumnematode/agent/agent.py
  • packages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.py
  • packages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/quantum-nematode/quantumnematode/agent/agent.py
  • openspec/changes/weight-search-architecture-ranking/design.md
  • packages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.py
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_legacy_nociception_klinotaxis_control.yml
  • packages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.py

📝 Walkthrough

Walkthrough

Phase 0 completes a sensor×reward ablation: adds a distal_chemo_contact_trigger reward mode, two predator-sensor module variants, runtime gating/STAM fixes to expose distal chemo signals, tests validating behavior, scenario/experiment YAMLs, and documentation locking the canonical sensor+reward pairing.

Changes

Phase 0 predator-sensing biology architecture ranking

Layer / File(s) Summary
Reward mode contract and distal-chemo-contact-trigger implementation
packages/quantum-nematode/quantumnematode/agent/agent.py, packages/quantum-nematode/quantumnematode/agent/reward_calculator.py, packages/quantum-nematode/tests/quantumnematode_tests/agent/test_reward_calculator.py
RewardConfig.reward_mode gains distal_chemo_contact_trigger. RewardCalculator.calculate_reward applies a continuous distal-chemo concentration penalty for gradient_proximity and distal_chemo_contact_trigger, and stacks contact damage when in danger. Tests verify distal-only penalty, stacked distal+contact, exclusion of distance-scaled evasion, and skipping the flat fallback.
Predator lateral gradient gating fix
packages/quantum-nematode/quantumnematode/agent/agent.py, packages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.py
Klinotaxis head-sweep and predator_lateral_gradient now trigger when either nociception_mode == KLINOTAXIS or predator_distal_mode == KLINOTAXIS. Regression tests assert population/non-population across gating combinations.
Sensory module variants: sparse-fix mechanosensation and composite biology module
packages/quantum-nematode/quantumnematode/brain/modules.py, packages/quantum-nematode/tests/quantumnematode_tests/brain/test_predator_modules.py
Adds PredatorBiologyCompositeModule (classical_dim=4; quantum encoding compresses to 3) and _predator_mechanosensation_klinotaxis_sparse_fix_core (substitutes distal concentration for strength off-contact). Both modules are registered and tested for dimensions, routing, masking, and quantum compression.
STAM channel resolution and STAM-dim inference
packages/quantum-nematode/quantumnematode/agent/stam.py, packages/quantum-nematode/quantumnematode/brain/modules.py, packages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.py
resolve_active_channels recognizes predator_biology_klinotaxis and activates predator_mechano and predator_distal; _infer_stam_dim_from_modules counts sparse/composite modules correctly and avoids double-counting. Parametrized tests assert STAM-dim agreement and composite channel activation.
Scenario and Phase 0 experiment YAML configs
configs/scenarios/pursuit/*.yml, docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/*
Sets reward_mode: distal_chemo_contact_trigger in pursuit scenarios and adds Phase 0 YAMLs covering legacy-nociception control, sparse-fix mechanosensation, composite single-channel ablation, and distal-chemo-contact-trigger variants used during Phase 0 runs.
Phase 0 results, logbook, and openspec updates
docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/README.md, openspec/changes/weight-search-architecture-ranking/*
Adds comprehensive Phase 0 README with experimental matrix, bug-forensics, final ranked results, canonical lock (B0.5: two-channel klinotaxis + distal_chemo_contact_trigger), reproducibility checklist, and updates design/proposal/spec/tasks to record Phase 0 outcomes and compute/project changes.

Estimated code review effort

ðŸŽŊ 3 (Moderate) | ⏱ïļ ~25 minutes

Possibly related PRs

Poem

🐰 I sniffed the maze, both near and far,

distal beacons showed the scar;
sparse and composite had their say,
B0.5 hopped out to lead the way.
Bugs squashed, rewards set — onward to play!

ðŸšĨ Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: locking a canonical predator-evasion sensor and reward configuration with measured performance improvement (+14pp over legacy).
Docstring Coverage ✅ Passed Docstring coverage is 82.86% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏ïļ Tip: You can configure your own custom pre-merge checks in the settings.

âœĻ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧊 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/phase0-predator-sensing-investigation

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

@codecov

codecov Bot commented May 28, 2026

Copy link
Copy Markdown

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.
@chrisjz
chrisjz marked this pull request as ready for review May 28, 2026 11:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

ðŸ§đ Nitpick comments (4)
openspec/changes/weight-search-architecture-ranking/design.md (1)

174-177: ⚡ Quick win

Keep design.md non-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 in spec.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 win

Pin reward_mode explicitly for reproducibility.

This config currently depends on an implicit default. For logbook-grade reproducibility, set reward_mode: default explicitly 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 win

Convert 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 PredatorParams

As 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 win

Add an explicit return type for _make_predator_env.

_make_predator_env is 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

ðŸ“Ĩ Commits

Reviewing files that changed from the base of the PR and between c98d54b and b855526.

⛔ Files ignored due to path filters (2)
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/per-seed-results.csv is excluded by !**/*.csv
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/summary-stats.csv is excluded by !**/*.csv
📒 Files selected for processing (20)
  • configs/scenarios/pursuit/lstmppo_small_predator_biology_klinotaxis.yml
  • configs/scenarios/pursuit/mlpppo_small_predator_biology_klinotaxis.yml
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/README.md
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_legacy_nociception_klinotaxis_control.yml
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_biology_klinotaxis_distal_chemo_contact_trigger.yml
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_composite_klinotaxis.yml
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_distal_chemo_contact_trigger_klinotaxis.yml
  • docs/experiments/logbooks/supporting/025-weight-search-architecture-ranking/phase-0/configs/mlpppo_small_predator_sparse_fix_klinotaxis.yml
  • openspec/changes/weight-search-architecture-ranking/design.md
  • openspec/changes/weight-search-architecture-ranking/proposal.md
  • openspec/changes/weight-search-architecture-ranking/specs/predator-sensing-biology/spec.md
  • openspec/changes/weight-search-architecture-ranking/tasks.md
  • packages/quantum-nematode/quantumnematode/agent/agent.py
  • packages/quantum-nematode/quantumnematode/agent/reward_calculator.py
  • packages/quantum-nematode/quantumnematode/agent/stam.py
  • packages/quantum-nematode/quantumnematode/brain/modules.py
  • packages/quantum-nematode/tests/quantumnematode_tests/agent/test_agent.py
  • packages/quantum-nematode/tests/quantumnematode_tests/agent/test_reward_calculator.py
  • packages/quantum-nematode/tests/quantumnematode_tests/agent/test_stam.py
  • packages/quantum-nematode/tests/quantumnematode_tests/brain/test_predator_modules.py

Comment thread packages/quantum-nematode/quantumnematode/agent/agent.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.
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

@chrisjz
chrisjz merged commit 0017d54 into main May 28, 2026
3 checks passed
@chrisjz
chrisjz deleted the feat/phase0-predator-sensing-investigation branch May 28, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant