Skip to content

Connectome-constrained PPO brain + Phase 6 Gate 1 close - #183

Merged
chrisjz merged 14 commits into
mainfrom
feat/connectome-ppo-brain
May 24, 2026
Merged

Connectome-constrained PPO brain + Phase 6 Gate 1 close#183
chrisjz merged 14 commits into
mainfrom
feat/connectome-ppo-brain

Conversation

@chrisjz

@chrisjz chrisjz commented May 24, 2026

Copy link
Copy Markdown
Member

Summary

  • Ships the first connectome-constrained brain (ConnectomePPOBrain) — PPO-trainable policy over the wild-type Cook 2019 hermaphrodite connectome (302 neurons, 3709 chemical synapses strict-masked + PPO-learnable, 1093 gap junctions symmetric fan-in normalised + non-learnable). Registered as connectomeppo through the plugin registry from PR feat(brain-architecture): plugin registry for brain architectures (Phase 6 T2 L1, partial) #182; 20 brains total.
  • Closes Phase 6 Gate 1 GO across all four sub-criteria. R2b reference run on klinotaxis foraging reaches 100% sustained success rate on the last 100 episodes, all three Gate 1 G1.c pass-criteria literally satisfied. Within 6 points of MLPPPO + LSTMPPO baselines on the same task / env / seed. Full evidence in logbook 023.
  • Plugin-developer guide (docs/architecture/plugin-developer-guide.md) documents the ≤ 6-file budget for adding a new architecture. Carry-forward findings (entropy schedule, config promotion, MLPPPO klinotaxis baseline) recorded as T4.0d/e/f planning sub-tasks. OpenSpec change archived.

Closes Gate 1

Criterion Status Evidence
G1.a — connectome loaded + cross-validated Logbook 022 (T1 close)
G1.b — registry instantiates MLP-PPO + connectome via same code path No per-arch branches in scripts/run_simulation.py
G1.c — PPO-on-connectome trains without NaNs, beats frozen control ≥ 10%, monotonic improvement R2b: 500 ep, 16.1× last-25 reward margin, 76% → 100% success
G1.d — migration regression byte-equivalent for MLPPPO + LSTMPPO In-process two-construct equivalence tests (from PR #182)

What's in the diff

  • New brain (packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py, 720 LOC + 420 LOC test): ConnectomePPOBrain + ConnectomeTopology + ConnectomePPOBrainConfig. Forward pass h = tanh(W_chem.T @ (M_chem * h) + G_gap.T @ h) iterated K=4 times (canonical klinotaxis pathway depth). Two sensing_mode variants (oracle 2-feature; klinotaxis 3-feature). Motor readout pools VB/DB/VA/DA → learnable 4×4 → 4 action logits. freeze_updates: true short-circuits PPO step for the paired control.
  • Six foraging configs: oracle + klinotaxis + frozen-control + low-entropy diagnostic + new MLPPPO klinotaxis baseline.
  • Logbook 023 + retroactive logbook 022 entry in docs/experiments/README.md.
  • Plugin-developer guide under docs/architecture/plugin-developer-guide.md with worked example.
  • Tracker updates: phase6-tracking/tasks.md T2.1–T2.10 ticked, Gate 1 GO decision recorded + linked; T4.0d/e/f carry-forward sub-tasks added (entropy schedule, low-entropy config promotion, MLPPPO klinotaxis baseline acknowledgement). docs/roadmap.md T2 row flipped to ✅ with logbook link; L0 + L1 MUST exit-criteria ticked.
  • OpenSpec archive: add-architecture-plugin-interface archived to 2026-05-24-add-architecture-plugin-interface; main specs updated (brain-architecture gains 7 ADDED requirements + 1 MODIFIED; new connectome-ppo-brain capability created with Purpose section filled in). Also fixes a pre-existing structural bug in brain-architecture/spec.md (orphaned ## MODIFIED Requirements header from an earlier archive that left requirements invisible to OpenSpec parsing).

Test plan

  • uv run pre-commit run --files <every changed file> — green on all 30 changed files (mdformat, markdownlint, ruff, ruff-format, pyright, tests)
  • uv run pytest -m "not nightly" packages/quantum-nematode/ — 3279 passed, 1 skipped, 2 xfailed (pre-existing stale YAMLs on main); zero new failures
  • openspec validate brain-architecture --strict + openspec validate connectome-ppo-brain --strict — both valid
  • Quick sanity: from quantumnematode.brain.arch import ConnectomePPOBrain; 'connectomeppo' in list_registered_brains()True; 20 registered names
  • G1.c paired-control evaluation: R2b reference run + 4-way comparison (R2 / R2b / R2c MLPPPO / R2d LSTMPPO); full per-50-ep curves in scratchpad
  • No absolute-path leaks (/Users/, /home/, C:\Users\, file:///) in any committed content
  • No planning-doc terminology (Tranche / Decision / Gate / Layer / Phase / Milestone) in implementation .py or config .yml files

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added ConnectomePPO brain (connectome-constrained PPO) and multiple ConnectomePPO foraging scenarios including frozen-control, oracle, klinotaxis, and low-entropy variants.
    • Plugin-based brain registry enabling decorator-driven architecture registration.
  • Documentation

    • Plugin developer guide, updated specs (brain-architecture & ConnectomePPO), experiment logbook, roadmap/tracker and archived design/proposal/task notes.
  • Tests

    • Comprehensive ConnectomePPO test suite covering topology, forward-pass, masking, and learning invariants.
  • Chores

    • Lint config updates for test docstring rules.

Review Change Stack

chrisjz and others added 12 commits May 24, 2026 13:51
…brain

Introduces ConnectomePPOBrain — a PPO brain whose forward-pass topology
is the Cook 2019 *C. elegans* hermaphrodite connectome:

- Chemical synapses are strict-masked: only the wild-type adjacency
  (3709 edges) carries learnable scalar weights; PPO updates are
  projected back onto that adjacency after every optimiser step.
- Gap junctions carry fixed Cook 2019 synapse counts, fan-in normalised
  symmetrically (G[i,j] / sqrt(d_i * d_j)) so the bidirectional physics
  G[a,b] == G[b,a] is preserved across training.
- Sensor projection: food-chemotaxis [strength, angle] features are
  injected onto ASEL/ASER/AWCL/AWCR/AWAL/AWAR sensory neurons via a
  learnable 2-by-6 gain matrix (the canonical Bargmann-lab klinotaxis
  pathway).
- Motor readout: VB/DB/VA/DA motor-class activations are mean-pooled
  into a 4-vector, then projected to the 4 DEFAULT_ACTIONS via a
  learnable 4-by-4 readout matrix.
- Forward-pass depth K defaults to 4 (the canonical klinotaxis pathway
  depth sensory → primary-interneuron → command-interneuron → motor;
  K=1 produces a degenerate output because the food signal cannot
  propagate to motor neurons in one chemical-synapse hop).
- ``freeze_updates: true`` flag drives the paired-control branch of the
  upcoming training-signal check: with it set, PPO step() is a no-op
  and weights stay byte-identical to construction.
- Spec correction: tightened the gap-junction fan-in normalisation
  scenario in specs/connectome-ppo-brain/spec.md to require symmetric
  scaling (the original row-only scaling was inconsistent with the
  symmetry requirement asserted by the adjacent scenario); raised the
  default forward_pass_depth from 1 to 4 with explanatory note (K=1
  produced degenerate variance).

Registration:
- @register_brain("connectomeppo", ...) on the brain class.
- BrainType.CONNECTOMEPPO added to the enum + BRAIN_TYPES Literal.
- ConnectomePPOBrain / ConnectomePPOBrainConfig re-exported from
  brain.arch.__init__.
- brain_factory._build_infra_kwargs gains a CONNECTOMEPPO branch
  (forwards only ``device``; the action count is fixed by the
  topology's 4-by-4 readout, not configurable via ``num_actions``).
- Registry-vs-enum consistency check at import time now passes with
  20 registered brains.

Tests (25 new in test_connectome_ppo.py):
- Construction loads the connectome and builds W_chem / M_chem / G_gap.
- Strict-mask invariant holds at init and after every PPO update.
- Gap junctions remain symmetric, non-learnable, byte-identical across
  training.
- Sensor projection routes to the canonical 6 sensory neurons.
- Motor readout aggregates all 4 motor classes (each non-empty).
- Forward pass is finite and has non-degenerate variance (mean variance
  ~1.6e-3 across 100 random food inputs).
- K is configurable; K=2 differs from K=4 outputs; K=0 raises.
- Soft-prior mode is a no-op for apply_weight_mask; strict mode zeros
  ~M_chem entries.
- freeze_updates skips the optimiser; the frozen brain still samples.
- Registry-instantiation produces a functionally identical brain.
- Topology Protocol learnable_parameters excludes G_gap.

Lint: added D101/D102 to the project-wide test-file ignore list (test
classes are pure containers + test method names are already descriptive;
matches the existing relaxed-docstring conventions for tests).

Verified: openspec validate --strict clean. Pre-commit clean across all
changed files (mdformat, markdownlint, ruff, pyright, tests). 226
affected tests pass + 1 intentional skip + 2 pre-existing xfails.
Runtime sanity-check confirms strict-mask invariant, gap-junction
symmetry, finite forward pass, and non-degenerate variance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…state guard

Three pre-evaluation review items applied:

I2 (performance): cache the post-K hidden state during the topology
forward pass. ConnectomeTopology now exposes a forward_with_hidden(x)
method that returns both (logits, h_after_K) in a single sweep through
the 302x302 chemical + gap-junction connectivity. run_brain and
_perform_ppo_update both consume it via forward_with_hidden, eliminating
the second under-mask connectome traversal that previously ran inside
the torch.no_grad() block. The forward() shim still returns logits only
for callers that don't need the hidden state.

run_brain wall-clock measured at 0.17 ms/call after refactor (~3.4
seconds of action-sampling overhead at 20k calls / 100-episode rollout).

I1 (dead code): remove the unused `self.pending_reward = reward`
assignment from run_brain. The attribute was set but never read
elsewhere, never declared in __init__, and not consumed by the PPO
update. The `reward` parameter on run_brain is now correctly marked
unused (ARG002) since rewards are delivered via learn().

I3 (silent fallback): tighten the learn() pending-state guard. The
previous `action=self._pending_action or 0` would silently substitute
FORWARD (action_idx=0) if learn() was called before any run_brain().
Replaced with an explicit RuntimeError that surfaces the out-of-order
use with a descriptive message. The outer `_pending_state is not None`
guard already prevents this in normal env-loop usage; the inner check
is defence-in-depth.

Verified: 25/25 connectome unit tests pass; pre-commit clean (ruff +
ruff-format + pyright + tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ConnectomePPOBrainConfig gains a sensing_mode field
(oracle | klinotaxis). In klinotaxis mode the brain consumes
[food_concentration, food_lateral_gradient, food_dconcentration_dt]
(3 features) instead of the oracle [strength, angle] pair (2),
matching the env-side klinotaxis sensory-module emission shape
without re-implementing head-sweep computation in the brain.

ConnectomeTopology.food_gains is sized to the per-mode feature
count, and preprocess() switches on the configured mode with
tanh-clipped scaling matching the env-side klinotaxis sensor.

ConnectomePPOBrainConfig is also added to the BrainConfigType
union in config_loader so Pydantic smart-union resolves YAML
files that set the connectome-specific fields correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eline runs

Six configs land together for the connectome-PPO klinotaxis
paired-control evaluation:

- connectome_ppo_oracle.yml + _frozen_control.yml — initial
  oracle-mode sanity baseline before the klinotaxis adapter
  (kept for reproducibility of the early-baseline run).
- connectome_ppo_klinotaxis.yml + _frozen_control.yml — main
  klinotaxis paired-control configs (sensing_mode=klinotaxis,
  STAM enabled, chemotaxis_mode=klinotaxis).
- connectome_ppo_klinotaxis_low_entropy.yml — diagnostic config
  with entropy_coef cut to 0.005 (vs 0.02). Used to verify that
  late-training drift seen at the higher entropy was an
  exploration-annealing artefact, not architectural.
- mlpppo_small_klinotaxis.yml — inferred MLPPPO baseline at
  the klinotaxis env scale: combines the established
  mlpppo_small_oracle PPO hyperparameters with the
  lstmppo_small_klinotaxis env/sensing settings + proprioception
  (heading needed for memoryless MLP to correlate dC/dt with
  movement).

All configs share seed 2026 and the same env layout for
apples-to-apples comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New docs/architecture/plugin-developer-guide.md walks through the
files-touched budget (≤ 6) and the step-by-step pattern for adding a
new brain architecture: module + decorator, BrainType enum member,
__init__ import, BrainConfigType union, optional infra-kwargs branch,
tests. Worked example uses a hypothetical TinyMLPBrain.

Cross-linked from:

- docs/roadmap.md L1 row in the layered-platform table
- packages/quantum-nematode/quantumnematode/brain/arch/__init__.py
  module docstring
- openspec/config.yaml context (bumped 19 → 20 brains and added the
  registry/guide pointer)
- AGENTS.md Key Directories (same bump and pointer)

The OpenSpec change tasks.md sections 8 and 9 are ticked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logbook 023 records the architecture-plugin-interface implementation
outcome and the first connectome-constrained PPO brain result.

Gate 1 closes GO across all four sub-criteria:

- G1.a connectome loaded + cross-validated (logbook 022).
- G1.b registry instantiates MLP-PPO + connectome through same code
  path (no per-arch branches in run_simulation.py).
- G1.c PPO-on-connectome learns klinotaxis: R2b reference run hits
  100% sustained success on last-100 episodes; all three literal
  pass-criteria satisfied (no NaN/Inf over 500 ep, 16.1x last-25
  reward margin over frozen-random-weights control, monotonic
  improvement 76% to 100%); within 6 points of MLPPPO + LSTMPPO
  baselines on the same task / env / seed.
- G1.d migration regression byte-equivalent on MLPPPO + LSTMPPO;
  registration-only migration on the other 17 changes no executing
  code.

docs/experiments/README.md gets entries for logbooks 022 and 023
(022 entry was missed at the time the previous tranche merged).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
phase6-tracking/tasks.md: tick T2.1 through T2.10 sub-tasks; record
Gate 1 GO decision with sub-criteria evidence pointers; link the Gate
1 decision to logbook 023.

docs/roadmap.md: flip the Phase 6 Tranche Tracker T2 row from
"not started" to complete with logbook link; record the Gate 1 GO
outcome in the Mid-phase decision gates section; tick the L0 and L1
MUST exit-criteria with pointers to logbooks 022 and 023.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Section 12 pre-merge verification complete:
- Pre-commit hooks pass on all 22 branch-touched files.
- Full quantum-nematode pytest suite: 3279 passed, 1 skipped, 2 xfailed
  (pre-existing stale YAMLs from main).
- openspec validate add-architecture-plugin-interface --strict clean.
- No new >100KB files; no absolute-path leaks in committed content.

12.6 (user authorisation for push / PR create) remains intentionally
unticked — that pause is the next required step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…asks

Three findings surfaced during T2's connectome-PPO klinotaxis
evaluation that T4 must consume before the L2 sweep starts:

- T4.0d Connectome PPO entropy schedule. R2 -> R2b empirical evidence
  in logbook 023: constant entropy_coef=0.02 triggers late-training
  drift; entropy_coef=0.005 (or an entropy decay schedule) eliminates
  it. T4's T4.connectome.* cells must pick a documented default.
- T4.0e Promote the connectome klinotaxis low-entropy config to
  canonical; the entropy=0.02 variant has documented drift and the
  low-entropy variant is the R2b reference run that closed Gate 1.
- T4.0f Acknowledge mlpppo_small_klinotaxis.yml (newly inferred at
  T2) as the canonical MLPPPO klinotaxis foraging baseline that
  T4.mlp_ppo.klinotaxis consumes.

A "Carry-forward from T2" preamble above the T4 planning sub-tasks
flags the trio for anyone picking up T4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ipped implementation

Pre-archive divergence audit + alignment. The OpenSpec artefacts had
drifted in a handful of places against what actually shipped during
the connectome-ppo-brain follow-up PR:

Spec / design / proposal updates:
- Forward-pass depth default K corrected to 4 in design.md (was K=1
  in two places + the Open Questions section). K=1 produces
  degenerate output under strict-mask; K=4 matches the canonical
  klinotaxis pathway depth.
- spec.md ConnectomePPOBrainConfig: forward_pass_depth default
  corrected to 4 (was 1).
- Added sensing_mode (oracle | klinotaxis) to the spec's required
  fields and updated the sensor-projection scenario to describe the
  per-mode food_gains matrix shape.
- Removed proprioception / AVAL/AVAR/AVBL/AVBR projection language
  from design.md + proposal.md + tasks.md 7.3: scoped in early
  design but not implemented in T2 (food chemotaxis only ships;
  proprioception / mechanosensation / nociception land in T3 + T4).
- Gap-junction normalisation description in design.md corrected to
  symmetric `G[i, j] / sqrt(d_i * d_j)` (was stale per-row).
- Dropped 'deferred to follow-up PR' notes from the ConnectomePPO
  scenarios in brain-architecture/spec.md (both PRs shipped).
- Flipped 'follow-up PR' future-tense language in proposal.md +
  tasks.md to retrospective; the change is ready to archive.
- Migration Plan + Rollback strategy in design.md rewritten to
  describe the in-process two-construct equivalence test that
  shipped (not the pickle-fixture path that was scoped out).
- Open Questions resolved (smoke-config seed = 2026; the np.allclose
  question is made moot by the Decision 5 scope amendment).
- LOC figures corrected: brain_factory.py shrinks 459 to 240 LOC
  (~220 removed), not the originally projected ~170 LOC / ~400
  removed (proposal.md + tasks.md 6.1 + logbook 023).
- Episode budget in design.md updated from 100 to 500 with the
  rationale (cheap forward pass enabled the longer run).

Tasks tick:
- 12.6 ticked (user-authorisation pause observed before push).

All tasks now ticked except none; OpenSpec change ready to archive
after push + PR merge. `openspec validate ... --strict` green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All 73 tasks complete (12.6 ticked after the user-authorisation pause
on 2026-05-24). The change folder moves to
openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/.

Spec deltas applied:
- openspec/specs/brain-architecture/spec.md gains 7 new requirements
  (plugin registry, BrainTopology + LearningRule Protocols, MLPPPO +
  LSTMPPO + 17-arch migration regression bar, registry-enum
  consistency, external Brain Protocol unchanged) and the existing
  Brain Type Registry requirement is rewritten to mention the
  ConnectomePPO entries + the StrEnum migration.
- openspec/specs/connectome-ppo-brain/ is created with the 4
  requirements that fully describe the new architecture.

Also restructures openspec/specs/brain-architecture/spec.md so its
Brain Type Registry + Module Exports requirements live inside the
canonical ## Requirements section (a pre-existing structural bug from
an earlier archive that left them under a stale ## MODIFIED
Requirements header — invisible to validate/list/archive). Added a
SHALL preamble to those two requirements plus Aggregation Pheromone
Sensing Modules so the spec passes strict validation.

openspec validate --strict green on both specs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new openspec/specs/connectome-ppo-brain/spec.md was created with
a TBD placeholder Purpose during the archive step. Replace it with
the real Purpose paragraph covering: connectome topology source +
strict-mask + gap-junction normalisation + sensing-mode variants +
motor readout + registration via brain-architecture + sensor-projection
scope boundary.

Mirrors the fill-in we did for connectome-substrate after T1's archive
(commit ae7b0d4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 24, 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: 112a22d2-87d3-4211-a759-d6f79d4b3766

📥 Commits

Reviewing files that changed from the base of the PR and between 4402206 and 6a74b1b.

📒 Files selected for processing (3)
  • openspec/specs/connectome-ppo-brain/spec.md
  • packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py
  • packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_connectome_ppo.py
✅ Files skipped from review due to trivial changes (1)
  • openspec/specs/connectome-ppo-brain/spec.md

📝 Walkthrough

Walkthrough

This PR implements ConnectomePPOBrain (connectome-constrained PPO), registers it in the brain plugin registry, adds a ConnectomeTopology with strict/soft chemical masking and optional gap-junctions, implements PPO training with freeze_updates, supplies tests and multiple scenario YAMLs (learning and frozen controls), and updates developer/design/spec/tracker documentation.

Changes

ConnectomePPO Brain Implementation

Layer / File(s) Summary
Brain Type Enum and Registry Wiring
packages/quantum-nematode/quantumnematode/brain/arch/dtypes.py, packages/quantum-nematode/quantumnematode/brain/arch/__init__.py, packages/quantum-nematode/quantumnematode/utils/brain_factory.py, packages/quantum-nematode/quantumnematode/utils/config_loader.py
Adds BrainType.CONNECTOMEPPO = "connectomeppo", extends BRAIN_TYPES, imports/exports ConnectomePPOBrain/ConnectomePPOBrainConfig so module-level @register_brain runs, adjusts _build_infra_kwargs for CONNECTOMEPPO, and includes ConnectomePPOBrainConfig in BrainConfigType.
ConnectomeTopology and ConnectomePPOBrain Implementation
packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py
Implements ConnectomeTopology (m_chem/w_chem adjacency and masked weight init, symmetric fan-in normalized g_gap, food sensor gain injection, motor pooling/readout, forward_with_hidden with K recurrent tanh steps, apply_weight_mask) and ConnectomePPOBrain (preprocess, run_brain sampling, rollout buffer, _perform_ppo_update with strict/soft masking and freeze_updates, critic and Adam optimizer).
ConnectomePPO Unit Tests
packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_connectome_ppo.py
New pytest coverage for connectome loading/shape invariants, strict-mask/soft-prior semantics, gap-junction symmetry/non-learnability, sensor/motor wiring, forward-pass numeric invariants, PPO update masking behavior, and freeze_updates paired-control checks.
Evaluation Scenario Configurations
configs/scenarios/foraging/connectomeppo_small_*.yml, configs/scenarios/foraging/mlpppo_small_klinotaxis.yml
Adds multiple foraging scenarios: connectomeppo klinotaxis learning (K=4), frozen-control variants (freeze_updates: true), low-entropy klinotaxis variant, oracle baseline, and an MLPPPO klinotaxis baseline; each defines brain config, PPO hyperparameters, rewards, satiety, environment, sensing/STAM, and foraging parameters.
Plugin Developer Documentation
docs/architecture/plugin-developer-guide.md
New developer guide describing how to add/register brain plugins via @register_brain, matching BrainType enum entries, wiring in brain/arch/__init__.py, config_loader updates, optional brain_factory infra kwargs, testing expectations, and extension points (BrainTopology/LearningRule).
Design, Specification, and Tracking Documentation
openspec/, docs/roadmap.md, docs/experiments/, AGENTS.md, pyproject.toml
Updates design/proposal/spec to finalize ConnectomePPO invariants (symmetric fan-in gap normalization, K=4 default, chemical masking semantics), publishes connectome-ppo-brain spec and logbook 023, marks Phase 6 Tranche 2 Gate 1 as GO in roadmap/tracker, increments agent count in AGENTS.md, and adjusts Ruff test lint ignores.

Sequence Diagram(s)

sequenceDiagram
  participant Env as Environment
  participant Brain as ConnectomePPOBrain
  participant Topo as ConnectomeTopology
  participant Crit as CriticHead
  participant Optim as PPOOptimizer

  Env->>Brain: run_brain(BrainParams)
  Brain->>Topo: preprocess -> forward_with_hidden(food_features)
  Topo->>Topo: K iterations: masked_chem + gap_junctions -> tanh updates
  Topo-->>Brain: logits, hidden
  Brain->>Crit: value = critic(hidden)
  Brain->>Env: sample action -> ActionData
  Note over Brain,Optim: replay buffer fills
  Brain->>Optim: _perform_ppo_update() when buffer full / episode end
  Optim->>Topo: recompute logits/values per minibatch
  Optim->>Optim: compute PPO loss + backprop + grad clip
  Optim->>Topo: if strict -> apply_weight_mask(topology.w_chem)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

🐰 A tiny hare hops through neuron trees,
Mapping food smells with masked synapse keys,
K steps of tanh, a frozen-control glance,
ConnectomePPO learns its foraging dance,
Cheery carrots cheer the testing breeze.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Connectome-constrained PPO brain + Phase 6 Gate 1 close' clearly and specifically summarizes the main changes: the addition of a ConnectomePPO brain and completion of Phase 6 Gate 1 requirements.
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/connectome-ppo-brain

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

@codecov

codecov Bot commented May 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.10239% with 29 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...atode/quantumnematode/brain/arch/connectome_ppo.py 90.65% 18 Missing and 9 partials ⚠️
...um-nematode/quantumnematode/utils/brain_factory.py 0.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@chrisjz
chrisjz marked this pull request as ready for review May 24, 2026 07:20

@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: 8

🤖 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 `@configs/scenarios/foraging/connectome_ppo_klinotaxis_frozen_control.yml`:
- Line 1: The filename connectome_ppo_klinotaxis_frozen_control.yml does not
follow the required {brain}_{size}[_{variant}]_{sensing}.yml pattern; rename it
so the first token is the brain (connectome), the second token is the size
(replace or map the current "ppo" token to the correct size identifier),
optionally append the variant (frozen_control) before the final sensing token,
and place the sensing token (klinotaxis) last (e.g.,
connectome_{size}[_frozen_control]_klinotaxis.yml); update any references to
this file in the repo to use the new filename.

In `@configs/scenarios/foraging/connectome_ppo_klinotaxis_low_entropy.yml`:
- Line 1: The scenario file
configs/scenarios/connectome_ppo_klinotaxis_low_entropy.yml violates the naming
contract {brain}_{size}[_{variant}]_{sensing}.yml because it omits the size and
places sensing before the trailing position; rename the file to include the size
and move sensing to the end (for example
connectome_ppo_R2b_klinotaxis_low_entropy.yml or
connectome_ppo_R2b_low_entropy_klinotaxis.yml depending on which token is the
sensing field) so the filename matches the pattern and update any references to
this filename in code or CI/docs.

In `@configs/scenarios/foraging/connectome_ppo_klinotaxis.yml`:
- Line 1: The filename connectome_ppo_klinotaxis.yml doesn't follow the required
pattern {brain}_{size}[_{variant}]_{sensing}. Rename the file to match that
convention (e.g., connectome_{size}[_variant]_klinotaxis.yml—replace {size} with
the appropriate size token and add an optional variant if needed), and update
all references to this scenario (scenario registries/loaders, imports, test
fixtures, CI or docs) so the new filename is used everywhere; ensure the
scenario loader still finds the file after renaming.

In `@configs/scenarios/foraging/connectome_ppo_oracle_frozen_control.yml`:
- Line 1: The filename connectome_ppo_oracle_frozen_control.yml does not match
the required pattern {brain}_{size}[_{variant}]_{sensing}.yml; rename it to
include an explicit size token and a terminal sensing token (and keep the
existing variant if intended), e.g. follow the pattern by changing
connectome_ppo_oracle_frozen_control.yml into something like
connectome_ppo_{size}_oracle_frozen_control_{sensing}.yml (replace {size} and
{sensing} with the appropriate concrete tokens used across configs).

In `@configs/scenarios/foraging/connectome_ppo_oracle.yml`:
- Line 1: The config filename connectome_ppo_oracle.yml is missing the required
{size} segment; rename the file to follow the convention
{brain}_{size}[_{variant}]_{sensing}.yml (for example
connectome_ppo_small_oracle_[sensing].yml or appropriate size/variant/sensing
tokens) and update every reference to this file in code and other configs
(search for connectome_ppo_oracle in loaders, scenario registries, import
statements, and CI/test fixtures) so the loader expecting the
{brain}_{size}[_{variant}]_{sensing}.yml pattern will find it.

In `@packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py`:
- Around line 324-338: The apply_weight_mask method currently mutates internal
state and takes a mode string, which violates the shared topology contract
expecting a pure projector of shape apply_weight_mask(weights) -> Tensor; change
apply_weight_mask in ConnectomeTopology and the connectome_ppo implementation to
accept a weights Tensor and return the masked Tensor (do not mutate internal
state). Preserve a thin compatibility wrapper if needed: keep the old mode-based
signature (apply_weight_mask(self, *, mode)) to call the new projector or raise
a clear error, but update the core masking logic (currently in
_project_chem_weights) to be a pure function that takes weights and returns
masked_weights; update any callers (including the other occurrence of
apply_weight_mask referenced in the diff) to use the projector form.
- Around line 432-437: The constructor for the ConnectomePPO brain currently
gives `device: DeviceType = DeviceType.CPU`, which weakens the explicit-device
invariant; change the __init__ signature in the ConnectomePPO class to require
an explicit device (remove the default), i.e. accept `device: DeviceType` (no
default) alongside `config: ConnectomePPOBrainConfig` and `action_set`
unchanged, and ensure any internal uses still reference that parameter; after
this change, update any callers (e.g., places that instantiate
ConnectomePPOBrain, including utils/brain_factory.py) to pass a DeviceType
explicitly.

In `@pyproject.toml`:
- Around line 92-93: Remove the special-case ignorances for pydocstyle codes
"D101" and "D102" that are currently applied to test files (the pattern
"/**/tests/**/*.py") in pyproject.toml so tests are subject to the same
NumPy-style docstring rules; locate the per-file-ignores or pydocstyle/ruff
ignore list that references "D101" and "D102" and either delete those codes from
the ignore entry for the tests pattern or remove the per-file-ignores block
entirely so D101/D102 are enforced for test modules and methods.
🪄 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: 16e9c397-4861-4d6c-ba78-0645b175f421

📥 Commits

Reviewing files that changed from the base of the PR and between 0a6c6c1 and 0c0f91c.

📒 Files selected for processing (29)
  • AGENTS.md
  • configs/scenarios/foraging/connectome_ppo_klinotaxis.yml
  • configs/scenarios/foraging/connectome_ppo_klinotaxis_frozen_control.yml
  • configs/scenarios/foraging/connectome_ppo_klinotaxis_low_entropy.yml
  • configs/scenarios/foraging/connectome_ppo_oracle.yml
  • configs/scenarios/foraging/connectome_ppo_oracle_frozen_control.yml
  • configs/scenarios/foraging/mlpppo_small_klinotaxis.yml
  • docs/architecture/plugin-developer-guide.md
  • docs/experiments/README.md
  • docs/experiments/logbooks/023-architecture-plugin-interface.md
  • docs/roadmap.md
  • openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/.openspec.yaml
  • openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/design.md
  • openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/notes/dispatcher-audit.md
  • openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/proposal.md
  • openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/specs/brain-architecture/spec.md
  • openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/specs/connectome-ppo-brain/spec.md
  • openspec/changes/archive/2026-05-24-add-architecture-plugin-interface/tasks.md
  • openspec/changes/phase6-tracking/tasks.md
  • openspec/config.yaml
  • openspec/specs/brain-architecture/spec.md
  • openspec/specs/connectome-ppo-brain/spec.md
  • packages/quantum-nematode/quantumnematode/brain/arch/__init__.py
  • packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py
  • packages/quantum-nematode/quantumnematode/brain/arch/dtypes.py
  • packages/quantum-nematode/quantumnematode/utils/brain_factory.py
  • packages/quantum-nematode/quantumnematode/utils/config_loader.py
  • packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_connectome_ppo.py
  • pyproject.toml

@@ -0,0 +1,71 @@
# Frozen-random-weights control variant of the connectome-PPO

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rename this frozen-control scenario file to the required naming convention.

connectome_ppo_klinotaxis_frozen_control.yml does not satisfy the mandated {brain}_{size}[_{variant}]_{sensing}.yml format.

As per coding guidelines, configuration files under configs/scenarios/**/*.yml must follow {brain}_{size}[_{variant}]_{sensing}.yml.

🤖 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 `@configs/scenarios/foraging/connectome_ppo_klinotaxis_frozen_control.yml` at
line 1, The filename connectome_ppo_klinotaxis_frozen_control.yml does not
follow the required {brain}_{size}[_{variant}]_{sensing}.yml pattern; rename it
so the first token is the brain (connectome), the second token is the size
(replace or map the current "ppo" token to the correct size identifier),
optionally append the variant (frozen_control) before the final sensing token,
and place the sensing token (klinotaxis) last (e.g.,
connectome_{size}[_frozen_control]_klinotaxis.yml); update any references to
this file in the repo to use the new filename.

@@ -0,0 +1,69 @@
# Connectome-PPO klinotaxis foraging — low-entropy variant for R2b

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Rename this scenario file to match the required configs/scenarios naming contract.

connectome_ppo_klinotaxis_low_entropy.yml does not match {brain}_{size}[_{variant}]_{sensing}.yml (size is missing and sensing is not in the trailing position), which can break convention-based tooling and indexing.

As per coding guidelines, "Configuration files in configs/scenarios must follow naming convention: {brain}{size}[{variant}]_{sensing}.yml".

🤖 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 `@configs/scenarios/foraging/connectome_ppo_klinotaxis_low_entropy.yml` at line
1, The scenario file configs/scenarios/connectome_ppo_klinotaxis_low_entropy.yml
violates the naming contract {brain}_{size}[_{variant}]_{sensing}.yml because it
omits the size and places sensing before the trailing position; rename the file
to include the size and move sensing to the end (for example
connectome_ppo_R2b_klinotaxis_low_entropy.yml or
connectome_ppo_R2b_low_entropy_klinotaxis.yml depending on which token is the
sensing field) so the filename matches the pattern and update any references to
this filename in code or CI/docs.

@@ -0,0 +1,77 @@
# Connectome-PPO klinotaxis-mode foraging configuration.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rename this scenario file to match the required scenario naming pattern.

connectome_ppo_klinotaxis.yml does not follow the required {brain}_{size}[_{variant}]_{sensing}.yml convention.

As per coding guidelines, configuration files under configs/scenarios/**/*.yml must follow {brain}_{size}[_{variant}]_{sensing}.yml.

🤖 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 `@configs/scenarios/foraging/connectome_ppo_klinotaxis.yml` at line 1, The
filename connectome_ppo_klinotaxis.yml doesn't follow the required pattern
{brain}_{size}[_{variant}]_{sensing}. Rename the file to match that convention
(e.g., connectome_{size}[_variant]_klinotaxis.yml—replace {size} with the
appropriate size token and add an optional variant if needed), and update all
references to this scenario (scenario registries/loaders, imports, test
fixtures, CI or docs) so the new filename is used everywhere; ensure the
scenario loader still finds the file after renaming.

@@ -0,0 +1,63 @@
# Frozen-random-weights control variant of the connectome-PPO oracle

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Filename token order violates the required scenario pattern.

connectome_ppo_oracle_frozen_control.yml is not in {brain}_{size}[_{variant}]_{sensing}.yml form (missing size and non-terminal sensing token), so it should be renamed to a compliant pattern.

Based on learnings, "all scenario configuration YAML files under configs/scenarios/ must be named using the pattern {brain}_{size}[_{variant}]_{sensing}.yml".

🤖 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 `@configs/scenarios/foraging/connectome_ppo_oracle_frozen_control.yml` at line
1, The filename connectome_ppo_oracle_frozen_control.yml does not match the
required pattern {brain}_{size}[_{variant}]_{sensing}.yml; rename it to include
an explicit size token and a terminal sensing token (and keep the existing
variant if intended), e.g. follow the pattern by changing
connectome_ppo_oracle_frozen_control.yml into something like
connectome_ppo_{size}_oracle_frozen_control_{sensing}.yml (replace {size} and
{sensing} with the appropriate concrete tokens used across configs).

@@ -0,0 +1,68 @@
# Connectome-PPO oracle-mode foraging configuration — early baseline.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Filename is missing the required {size} segment.

connectome_ppo_oracle.yml does not satisfy {brain}_{size}[_{variant}]_{sensing}.yml, so this should be renamed (and references updated) to keep config loading and conventions consistent.

As per coding guidelines, "Configuration files in configs/scenarios must follow naming convention: {brain}{size}[{variant}]_{sensing}.yml".

🤖 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 `@configs/scenarios/foraging/connectome_ppo_oracle.yml` at line 1, The config
filename connectome_ppo_oracle.yml is missing the required {size} segment;
rename the file to follow the convention
{brain}_{size}[_{variant}]_{sensing}.yml (for example
connectome_ppo_small_oracle_[sensing].yml or appropriate size/variant/sensing
tokens) and update every reference to this file in code and other configs
(search for connectome_ppo_oracle in loaders, scenario registries, import
statements, and CI/test fixtures) so the loader expecting the
{brain}_{size}[_{variant}]_{sensing}.yml pattern will find it.

Comment thread packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py Outdated
Comment thread pyproject.toml
Comment on lines +92 to +93
"D101", # Some test classes are pure containers — docstring not informative
"D102", # Test method names already describe the assertion

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate ruff pydocstyle / docstring policy in pyproject.toml
ls -la
rg -n "ruff|pydocstyle|D101|D102|ignore|per-file-ignores" -S pyproject.toml || true

# Show the relevant section around the reported lines
nl -ba pyproject.toml | sed -n '70,120p'

# Search repo for docstring policy language (NumPy-style, **/*.py, etc.)
rg -n "NumPy-style|NumPy doc|docstring|pydocstyle|D101|D102|\\*\\*/\\*\\.py|\\*\\*\\/\\*\\.py|contributing|coding guideline" -S . || true

Repository: SyntheticBrains/nematode

Length of output: 4687


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show the relevant pyproject.toml sections around per-file ignores and pydocstyle
echo "---- pyproject.toml per-file-ignores context ----"
awk 'NR>=70 && NR<=110 {print NR ":" $0}' pyproject.toml

echo "---- pyproject.toml pydocstyle config context ----"
awk 'NR>=130 && NR<=190 {print NR ":" $0}' pyproject.toml

# 2) Search for repo-wide docstring policy language
rg -n "NumPy-style|NumPy docstring|pydocstyle|D101|D102|docstring.*\\*\\*/\\*\\.py|\\*\\*/\\*\\.py|docstrings? policy|coding guideline|conventions" \
  README.md CONTRIBUTING.md AGENTS.md docs -S || true

# 3) Check if tests are mentioned as exempt anywhere in those docs
rg -n "test(s)?|tests|except|exempt|ignore" \
  README.md CONTRIBUTING.md AGENTS.md docs -S || true

Repository: SyntheticBrains/nematode

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- AGENTS.md around NumPy-style docstrings ----"
awk 'NR>=45 && NR<=75 {print NR ":" $0}' AGENTS.md

echo "---- CONTRIBUTING.md around NumPy-style docstrings ----"
# Find the exact line containing the guideline then print a small window.
line=$(rg -n "NumPy-style docstrings|Docstrings.*NumPy|Use NumPy-style docstrings" CONTRIBUTING.md | head -n1 | cut -d: -f1 || true)
if [ -n "${line:-}" ]; then
  start=$((line-10)); end=$((line+30));
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {print NR ":" $0}' CONTRIBUTING.md
else
  rg -n "NumPy-style docstrings|Docstrings.*NumPy|Use NumPy-style docstrings" CONTRIBUTING.md -n || true
fi

echo "---- Search for explicit '**/*.py' mention ----"
rg -n "\\*\\*/\\*\\.py|\\*\\*\\/\\*\\.py|\\*\\*\\/\\*\\.py|\\*\\*/\\*\\.py|\\*\\*\\/\\*\\.py" AGENTS.md CONTRIBUTING.md -S || true

echo "---- Search for docstring exemptions/exclude tests ----"
rg -n "docstring.*test|tests.*docstring|exclude.*tests|exempt.*tests|tests are exempt|per-file-ignores|ruff.*pydocstyle" AGENTS.md CONTRIBUTING.md pyproject.toml -S || true

Repository: SyntheticBrains/nematode

Length of output: 3511


🏁 Script executed:

#!/bin/bash
set -euo pipefail
awk 'NR>=110 && NR<=150 {print NR ":" $0}' CONTRIBUTING.md

Repository: SyntheticBrains/nematode

Length of output: 1489


Remove Ruff D101/D102 exceptions for tests to keep NumPy docstring policy consistent

AGENTS.md and CONTRIBUTING.md require NumPy-style docstrings without excluding tests, but pyproject.toml config disables pydocstyle D101/D102 for /**/tests/**/*.py, creating a policy/tooling mismatch.

Suggested change
 "/**/tests/**/*.py" = [
   "ANN",
   "ARG",
   "D100",
-  "D101",  # Some test classes are pure containers — docstring not informative
-  "D102",  # Test method names already describe the assertion
   "D104",
   "PLR2004",
   "S101",
   "SLF001",  # Tests can access private members for testing
   "T201",
 ]
🤖 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 `@pyproject.toml` around lines 92 - 93, Remove the special-case ignorances for
pydocstyle codes "D101" and "D102" that are currently applied to test files (the
pattern "/**/tests/**/*.py") in pyproject.toml so tests are subject to the same
NumPy-style docstring rules; locate the per-file-ignores or pydocstyle/ruff
ignore list that references "D101" and "D102" and either delete those codes from
the ignore entry for the tests pattern or remove the per-file-ignores block
entirely so D101/D102 are enforced for test modules and methods.

Two reviewer findings addressed; three skipped with rationale below.

(1) Rename connectome configs to match the established
    `{brain}_{size}[_{variant}]_{sensing}.yml` convention from AGENTS.md.
    The reviewer parsed `connectome_ppo_*` as `{brain=connectome}_{size=ppo}`,
    which is wrong — the brain token is `connectomeppo` (the registered
    name in the plugin registry). Rename clarifies that and aligns with
    every other config in the directory:

    connectome_ppo_klinotaxis.yml
      → connectomeppo_small_klinotaxis.yml
    connectome_ppo_klinotaxis_frozen_control.yml
      → connectomeppo_small_frozen_control_klinotaxis.yml
    connectome_ppo_klinotaxis_low_entropy.yml
      → connectomeppo_small_low_entropy_klinotaxis.yml
    connectome_ppo_oracle.yml
      → connectomeppo_small_oracle.yml
    connectome_ppo_oracle_frozen_control.yml
      → connectomeppo_small_frozen_control_oracle.yml

    Variant tokens (`frozen_control`, `low_entropy`) sit between size and
    sensing, matching `crhqlstm_small_classical_oracle.yml` etc.

    Live references updated: logbook 023, phase6-tracking T4.0e, the
    configs' own cross-refs, and the mlpppo klinotaxis baseline's
    cross-ref. Archived OpenSpec change docs are left untouched
    (immutable historical record).

(2) Fix Protocol-conformance bug on `ConnectomeTopology.apply_weight_mask`.
    The `BrainTopology` Protocol specifies a pure projector:
    `apply_weight_mask(weights: torch.Tensor) -> torch.Tensor`. The
    previous signature `apply_weight_mask(*, mode: str) -> None`
    mutated `self.w_chem.data` in-place and conflated the structural
    projector with the brain's mask-mode policy.

    `ConnectomeTopology.apply_weight_mask(weights)` is now stateless —
    returns `weights * M_chem`. The brain's update loop owns the policy:
    under `chemical_mask_mode="strict"` it copies the projected tensor
    back into `topology.w_chem.data`; under `"soft_prior"` it skips the
    projection entirely. The `_project_chem_weights` helper is removed;
    its single internal use at construction-time now goes through the
    public projector too.

    Tests updated: `test_soft_prior_apply_mask_is_noop` is replaced
    with two tests — `test_apply_weight_mask_is_pure_projector` (asserts
    the topology's projector is stateless + returns the right tensor)
    and `test_soft_prior_mode_does_not_project_after_ppo_step` (asserts
    the brain's policy honours the mode by driving a PPO update under
    soft-prior + checking ~M_chem weights survive).

Skipped reviewer findings with rationale:

- Make `device` non-default on `ConnectomePPOBrain.__init__`. Skipped:
  every other PPO brain in the codebase (mlpppo, lstmppo, mlpreinforce)
  defaults `device: DeviceType = DeviceType.CPU`. Diverging would
  violate the established constructor convention.

- Remove D101/D102 from per-file-ignores for tests. Skipped: these
  ignores were added intentionally in commit b025677 with a documented
  rationale ("test classes are pure containers + test method names are
  already descriptive"); they sit alongside the same test-relaxation
  pattern used for D100/D104/ANN/ARG/S101/PLR2004/T201 throughout the
  codebase. Removing them would diverge from the consistent
  test-vs-implementation docstring policy.

Verification:
- `uv run pytest -m "not nightly" packages/quantum-nematode/` →
  3280 passed (the soft-prior tests went 1 → 2, so total +1 vs main),
  1 skipped, 2 xfailed (pre-existing stale YAMLs).
- `uv run pre-commit run --files <changed>` → all hooks green.
- `openspec validate brain-architecture --strict` /
  `openspec validate connectome-ppo-brain --strict` /
  `openspec validate phase6-tracking --strict` → all valid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@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 (2)
packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py (1)

320-331: ⚡ Quick win

Use NumPy-style sections in the new method docstring

Please convert this new docstring to NumPy-style (Parameters, Returns, etc.) to match repository standards.

As per coding guidelines, **/*.py: Use NumPy-style docstrings for documenting functions and classes.

🤖 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/quantumnematode/brain/arch/connectome_ppo.py`
around lines 320 - 331, Update the docstring for apply_weight_mask to
NumPy-style: add a "Parameters" section documenting weights : torch.Tensor and
its description, and a "Returns" section documenting the returned torch.Tensor
and its description (e.g., projected weights with chemical-synapse edges
zeroed); keep the existing high-level explanation about strict-mask behavior and
purity but convert prose into concise NumPy sections and ensure type hints and
intent (stateless, no in-place mutation) remain mentioned in either the Returns
or Notes section as appropriate.
packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_connectome_ppo.py (1)

318-323: ⚡ Quick win

Normalize new test method docs to NumPy style

The newly added test docstrings (and helper method docs) should use NumPy-style sections for consistency with the repo rule.

As per coding guidelines, **/*.py: Use NumPy-style docstrings for documenting functions and classes.

Also applies to: 339-346, 365-379

🤖 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/brain/arch/test_connectome_ppo.py`
around lines 318 - 323, Update the docstrings for the new test and its helper
methods to NumPy-style: replace the current free-form/Google-style descriptions
in test_apply_weight_mask_is_pure_projector (and the other affected methods
around the same change) with NumPy sections such as Parameters, Returns, and
Notes/Examples as appropriate so they conform to the project's NumPy-style
docstring convention; ensure the test purpose remains the same (describe that
ConnectomeTopology.apply_weight_mask is a stateless projector returning weights
* M_chem without mutating self) and that any helper method docs similarly use
NumPy section headers and brief descriptions.
🤖 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/brain/arch/connectome_ppo.py`:
- Around line 713-722: The forward pass currently multiplies weights by the mask
unconditionally (masked_chem = self.w_chem * self.m_chem), which zeroes
gradients on masked edges and prevents the "soft_prior" mode from growing new
edges; change the forward logic so that the mask multiplication is only applied
when self.config.chemical_mask_mode == "strict" and for "soft_prior" use the raw
self.w_chem (so gradients flow through masked entries), while keeping the
existing strict post-optimizer projection that calls
topology.apply_weight_mask(self.topology.w_chem.data) intact; update references
in the forward method to use masked_chem (or raw) depending on
self.config.chemical_mask_mode and ensure topology.w_chem / self.w_chem
semantics remain consistent.

---

Nitpick comments:
In `@packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py`:
- Around line 320-331: Update the docstring for apply_weight_mask to
NumPy-style: add a "Parameters" section documenting weights : torch.Tensor and
its description, and a "Returns" section documenting the returned torch.Tensor
and its description (e.g., projected weights with chemical-synapse edges
zeroed); keep the existing high-level explanation about strict-mask behavior and
purity but convert prose into concise NumPy sections and ensure type hints and
intent (stateless, no in-place mutation) remain mentioned in either the Returns
or Notes section as appropriate.

In
`@packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_connectome_ppo.py`:
- Around line 318-323: Update the docstrings for the new test and its helper
methods to NumPy-style: replace the current free-form/Google-style descriptions
in test_apply_weight_mask_is_pure_projector (and the other affected methods
around the same change) with NumPy sections such as Parameters, Returns, and
Notes/Examples as appropriate so they conform to the project's NumPy-style
docstring convention; ensure the test purpose remains the same (describe that
ConnectomeTopology.apply_weight_mask is a stateless projector returning weights
* M_chem without mutating self) and that any helper method docs similarly use
NumPy section headers and brief descriptions.
🪄 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: b9f84529-53df-4edd-917b-5460d09d1341

📥 Commits

Reviewing files that changed from the base of the PR and between 0c0f91c and 4402206.

📒 Files selected for processing (10)
  • configs/scenarios/foraging/connectomeppo_small_frozen_control_klinotaxis.yml
  • configs/scenarios/foraging/connectomeppo_small_frozen_control_oracle.yml
  • configs/scenarios/foraging/connectomeppo_small_klinotaxis.yml
  • configs/scenarios/foraging/connectomeppo_small_low_entropy_klinotaxis.yml
  • configs/scenarios/foraging/connectomeppo_small_oracle.yml
  • configs/scenarios/foraging/mlpppo_small_klinotaxis.yml
  • docs/experiments/logbooks/023-architecture-plugin-interface.md
  • openspec/changes/phase6-tracking/tasks.md
  • packages/quantum-nematode/quantumnematode/brain/arch/connectome_ppo.py
  • packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_connectome_ppo.py
💤 Files with no reviewable changes (1)
  • configs/scenarios/foraging/connectomeppo_small_oracle.yml
✅ Files skipped from review due to trivial changes (3)
  • configs/scenarios/foraging/connectomeppo_small_frozen_control_klinotaxis.yml
  • docs/experiments/logbooks/023-architecture-plugin-interface.md
  • configs/scenarios/foraging/mlpppo_small_klinotaxis.yml

The forward pass unconditionally multiplied ``w_chem`` by ``m_chem``,
which made the soft-prior mode silently equivalent to strict: backprop
multiplied the upstream gradient by ``m_chem``, pinning gradients on
non-wild-type entries to exactly zero. With zero gradient and zero
initialisation, the optimiser could never grow new edges — so the
"PPO is free to grow new connections" guarantee soft-prior promised
was never actually delivered.

Make the mask-or-no-mask choice a structural property of the topology
(``enforce_strict_mask: bool`` constructor kwarg) so the forward pass
branches:

- strict: ``chem_mat = w_chem * m_chem``  → gradients pinned to wild-type
- soft_prior: ``chem_mat = w_chem``       → gradients flow everywhere

The brain passes ``enforce_strict_mask=(config.chemical_mask_mode ==
"strict")`` into the topology at construction. The post-optimiser
projection in the brain's update loop (under strict mode) stays as
defence-in-depth — combined with the forward-pass masking, the
strict-mask invariant is guaranteed by two independent mechanisms.

Tests added:

- ``test_soft_prior_gradients_flow_through_non_wild_type_edges`` — under
  soft-prior, backprop produces non-zero gradient on at least one
  ~M_chem entry.
- ``test_strict_mode_pins_gradients_to_wild_type_edges`` — under strict,
  backprop produces exactly-zero gradient on every ~M_chem entry.

The strict-mask invariant test (post-PPO-update ``w_chem * ~m_chem ==
0``) continues to pass; under the new mechanism the invariant is
maintained primarily by the forward-pass masking rather than the
post-step projection (the projection is now redundant defence under
strict and skipped under soft-prior).

Spec updated: the "Chemical-synapse strict-mask" and "Soft-prior mode"
scenarios in openspec/specs/connectome-ppo-brain/spec.md now describe
the forward-pass gradient-flow mechanism, not just the post-step
projection.

Verification:
- ``uv run pytest -m "not nightly" packages/quantum-nematode/`` →
  3282 passed (+2 from the new gradient-flow tests), 1 skipped,
  2 xfailed (pre-existing).
- ``uv run pre-commit run --files <changed>`` → all hooks green.
- ``openspec validate connectome-ppo-brain --strict`` → valid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant