Skip to content

feat: Transformer (temporal-window) PPO brain — Gate-2 plugin parity (§5) - #212

Merged
chrisjz merged 8 commits into
mainfrom
openspec/t5-transformer-gate2
Jun 6, 2026
Merged

feat: Transformer (temporal-window) PPO brain — Gate-2 plugin parity (§5)#212
chrisjz merged 8 commits into
mainfrom
openspec/t5-transformer-gate2

Conversation

@chrisjz

@chrisjz chrisjz commented Jun 6, 2026

Copy link
Copy Markdown
Member

Summary

§5 of add-continuous-2d-and-action-heads: add a Transformer self-attention PPO brain as the Gate-2 plugin-parity vehicle (G2.b/G2.c) and the roadmap's MAY transformer comparison row. The trunk is a Transformer encoder over a temporal window of recent sensory feature vectors — an attention-based temporal-memory comparator alongside LSTM/CfC. Both action heads (discrete + continuous via the shared _policy).

Commit structure (so the G2.b parity diff is clean)

  1. refactor(run-sim) — registry-driven CLI-default brain config (removes the per-arch match brain_type branch at the entrypoint). One-time platform fix, separate prior commit, not counted in G2.b.
  2. feat(transformer) — the measured transformer-addition: 5 files (â‰Ī 6).
  3. docs(transformer) — klinotaxis config + 24→25 enumerations + task ticks (outside the parity diff).
  4. Follow-ups: format, D8 sync, review fix.

Gate-2 results

  • G2.b (â‰Ī 6 files): PASS — 5 files (git show --name-only on commit 2): new module, dtypes.py enum, brain/arch/__init__.py, config_loader.py BrainConfigType union, test. No _build_infra_kwargs branch (default infra shape); BRAIN_CONFIG_MAP auto-derives from the registry.
  • G2.c (no per-arch branches): PASS — registry-driven CLI default + env-mode action dispatch; no isinstance(brain, â€Ķ) added.
  • G2.a (engineer-hours): recorded, non-load-bearing (formalized in §7).
  • G2.d (floor check): §6 (pending) — connectome + MLP â‰Ĩ 50 % of T4 baseline.

Design choices

  • Window-as-state: each step stores its own front-zero-padded (window_size, input_dim) window in the shared shuffled-minibatch RolloutBuffer; the update re-forwards the stored window. Self-contained entries → no recurrent BPTT buffer despite being temporal.
  • Both heads via _policy; learnable positional embedding; shared encoder + separate actor/critic heads.
  • 5.3 also fixed a latent CLI bug (the old match listed mlpppo, which can't construct seed-only, and omitted connectomeppo).

Validation

  • Learning signal (§5.5): 200-run discrete klinotaxis — quarter-mean return 5.2 → 36.2 (last > first), foods/episode 3.1 → 9.9 of 10 (196/200 runs ate â‰Ĩ1), no NaN/Inf. A clean monotonic curve.
  • 9 unit tests (registration, head shapes, window padding/reset, discrete-grid + continuous-2D smokes, weight round-trip, config validator). Full non-nightly suite + lint + pyright green.

OpenSpec

§5.1–5.6 ticked; D8 carries the implementation-outcome note; openspec validate --strict passes.

ðŸĪ– Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added TransformerPPO brain: Transformer-based temporal-attention policy with PPO, supporting discrete and continuous actions.
    • Added a ready-to-run TransformerPPO foraging scenario (klinotaxis).
  • Documentation

    • Updated docs to list 25 brain architectures and added the TransformerPPO entry.
  • Tests

    • Added tests covering registration, head shapes, temporal-window behavior, end-to-end training, and weight round-trip.

chrisjz and others added 7 commits June 6, 2026 23:47
â€Ķiew)

Pre-implementation readiness review of §5 (Gate-2 transformer parity). Verified
the plan against current code (â‰Ī6-file budget holds — config map auto-derived
but BrainConfigType union + 5 other sites stand; CLI-default match block still
needs the 5.3 registry refactor; 24 brains → 24→25 correct). Settled the two
open design choices:

- Transformer input = temporal window (rolling buffer of last N feature vectors
  → encoder), an attention-based temporal-memory comparator alongside LSTM/CfC.
- Both action heads: discrete (G2.b/G2.c parity) + continuous tanh-Gaussian via
  the shared _policy (no extra files; continuous arm joins the §6 tuning).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
â€Ķrch branch)

Replace the `match brain_type` CLI-default block in run_simulation.py with a
registry-driven default: `get_registration(brain_type.value).config_cls(seed=â€Ķ)`.
Removes the per-architecture branch at the entrypoint (the G2.c "no per-arch
branches" claim must hold here too), so a new architecture — e.g. the §5
transformer — needs no `case` here.

One-time platform fix, landed as a SEPARATE prior commit so it is NOT counted in
the transformer-addition `git diff --name-only` that measures the â‰Ī6-file
plugin-parity budget (G2.b). §5.3.

Behaviour preserved: `brain_type` defaults to `DEFAULT_BRAIN_TYPE` (qvarcircuit,
seed-only-constructible) and is overwritten from the config file below, so config
runs are unaffected (verified). Brains whose config requires fields (e.g.
`sensory_modules`) must be run via `--config` — the seed-only default now raises a
clear pydantic ValidationError instead of the previous stale silent fall-through
(the match listed `mlpppo`, which can't construct seed-only, and omitted
`connectomeppo`, which can).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
â€Ķarity (§5.1/§5.2)

Add a Transformer self-attention PPO brain as the Gate-2 plugin-parity vehicle
(G2.b/G2.c). The policy/value trunk is a Transformer encoder over a temporal
window of the last N sensory feature vectors — an attention-based temporal-memory
comparator alongside LSTM/CfC. Both action heads: discrete (Categorical) and
continuous (tanh-squashed Gaussian) via the shared `_policy` helpers.

Window-as-state: each step stores its own front-zero-padded (window_size,
input_dim) window in the shared RolloutBuffer, so each entry is self-contained
and the update re-forwards the stored window — reusing the shuffled-minibatch
buffer (the window *is* the temporal context; no recurrent BPTT chain).

Plugin-parity (G2.b): the addition touches exactly 5 files (â‰Ī 6) — new module,
`dtypes.py` enum + Literal, `brain/arch/__init__.py` import/export,
`config_loader.py` BrainConfigType union, and the test. NO `_build_infra_kwargs`
branch (the brain matches the default infra shape) and NO per-architecture branch
anywhere (registry-driven CLI default landed separately in the prior commit) —
G2.c. The YAML config example + docs enumerations land in separate commits,
outside the measured diff.

Tests (8): registration, discrete/continuous head shapes, front-padded window +
per-episode reset, discrete-grid + continuous-2D training smokes (no NaN, worm
moves), weight round-trip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Outside the G2.b parity diff (separate from the measured transformer-addition
commit):

- Ship `transformerppo_small_klinotaxis.yml` (discrete grid) — the §5.5 smoke.
  200-run headless training shows a clean learning signal: quarter-mean return
  5.2 → 36.2 (last > first) and foods/episode 3.06 → 9.94 (≈ target 10; 196/200
  runs ate â‰Ĩ1), no NaN/Inf.
- Bump the brain-arch enumerations 24 → 25 + add `transformerppo`: AGENTS.md,
  README.md, CONTRIBUTING.md, openspec/config.yaml (§5.6 — platform bookkeeping,
  not per-architecture parity cost).
- Tick §5.1–5.6 with outcomes. G2.b measured = 5 files (â‰Ī6); G2.c = no
  per-architecture branches added; the GO/PIVOT/STOP verdict lands in §7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cosmetic only — ruff-format wasn't run on the new file before the commit-B
snapshot. No logic change; G2.b's 5-file measurement (on the isolated addition
commit) is unaffected (this touches an already-counted file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
D8 captured the parity mechanics but was silent on the realized implementation.
Add an implementation note: G2.b realized = 5 files (no _build_infra_kwargs
branch, no BRAIN_CONFIG_MAP edit — auto-derived); architecture = temporal-window
self-attention with window-as-state reusing the shared RolloutBuffer (no BPTT
buffer); 5.3 also fixed a stale CLI match (mlpppo/connectomeppo); transformer
learns cleanly on klinotaxis (bonus).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
â€Ķiew)

Branch review: an invalid d_model/nhead combination raised a raw torch
AssertionError ('embed_dim must be divisible by num_heads') deep in brain
construction. Add a pydantic model_validator (matching the other brain configs)
so it fails fast with a clear message at config time; also guards window_size>=1.
Test added.

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

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

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

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dcefd2eb-61c3-4ee7-8adc-7f38f3acc23f

ðŸ“Ĩ Commits

Reviewing files that changed from the base of the PR and between face52d and 650d20f.

📒 Files selected for processing (2)
  • packages/quantum-nematode/quantumnematode/brain/arch/transformer_ppo.py
  • packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_transformer_ppo.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_transformer_ppo.py
  • packages/quantum-nematode/quantumnematode/brain/arch/transformer_ppo.py

📝 Walkthrough

Walkthrough

Adds a TransformerPPO brain: a Transformer-encoder PPO agent over rolling temporal windows, integrated into enums, config loading, registry-driven runtime selection, tests, an example foraging scenario, and updated docs/specs enumerating 25 brain architectures.

Changes

TransformerPPO Brain Architecture

Layer / File(s) Summary
Type System and Registry Foundation
packages/quantum-nematode/quantumnematode/brain/arch/dtypes.py, packages/quantum-nematode/quantumnematode/brain/arch/__init__.py, packages/quantum-nematode/quantumnematode/utils/config_loader.py, scripts/run_simulation.py
Adds TRANSFORMER_PPO to BrainType/BRAIN_TYPES, exports TransformerPPOBrain/TransformerPPOBrainConfig, includes the config class in BrainConfigType, and switches runtime default brain-config selection to the registry.
TransformerPPO Brain Implementation
packages/quantum-nematode/quantumnematode/brain/arch/transformer_ppo.py
Implements TransformerPPOBrainConfig (validators) and TransformerPPOBrain: input projection, positional embedding, TransformerEncoder trunk, actor/critic heads (discrete & continuous), temporal windowing, rollout buffer and PPO update loop, weight persistence, and lifecycle hooks.
Test Suite
packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_transformer_ppo.py
Adds registration and wiring tests, head-shape and config-validation tests, temporal-window behavior tests, grid and continuous-2D smoke training tests, and weight round-trip serialization tests.
Configuration Example and Public Documentation
configs/scenarios/foraging/transformerppo_small_klinotaxis.yml, README.md, CONTRIBUTING.md, AGENTS.md
Adds a TransformerPPO klinotaxis foraging scenario and updates user-facing docs to list transformerppo and increment the documented brain architecture count to 25.
Project Specification Updates
openspec/config.yaml, openspec/changes/add-continuous-2d-and-action-heads/design.md, openspec/changes/add-continuous-2d-and-action-heads/tasks.md
Updates openspec context to register transformerppo, expands design notes about the temporal-window Transformer choice and file-count, and marks transformer/plugin-parity tasks as completed.

Estimated code review effort

ðŸŽŊ 4 (Complex) | ⏱ïļ ~45 minutes

Possibly related PRs

Poem

🐰 A Transformer hops in, keen and spry,
Windowed memories under encoder sky,
Actor and critic hum PPO rhyme,
Buffer and updates tick in time,
Now twenty-five brains crowd the nematode by!

ðŸšĨ 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 PR title accurately captures the main addition: a Transformer PPO brain with temporal-window self-attention for Gate-2 plugin parity validation.
Docstring Coverage ✅ Passed Docstring coverage is 93.55% 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 openspec/t5-transformer-gate2

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

@chrisjz
chrisjz marked this pull request as ready for review June 6, 2026 14:33

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

ðŸĪ– 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 `@openspec/changes/add-continuous-2d-and-action-heads/design.md`:
- Line 89: The implementation note header currently reads "Implementation note
(2026-06-07):" which is a future date; update that header in the document (the
line containing "Implementation note (2026-06-07):") to either the actual
completion date "2026-06-06" or remove the parenthetical date entirely so the
record is not out-of-order.

In `@packages/quantum-nematode/quantumnematode/brain/arch/transformer_ppo.py`:
- Around line 139-160: TransformerPPOBrain currently accepts a mutable
DEFAULT_ACTIONS list by reference (action_set parameter, self._action_set, and
the action_set getter/setter), which lets callers mutate a shared list across
instances; fix this by changing the action_set default to None (or accept an
iterable) and ensure the constructor makes a defensive copy and stores an
immutable sequence (e.g., tuple(self._action_set) or
list(self._action_set).copy()), and update the action_set property to return an
immutable copy and to store a defensive copy on set so mutations cannot leak
between instances; refer to TransformerPPOBrain, the action_set parameter,
self._action_set, and the action_set getter/setter when making the changes.

In
`@packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_transformer_ppo.py`:
- Around line 23-37: Add a NumPy-style docstring to the _config function:
describe the purpose of the helper, document the parameter continuous (bool)
under Parameters with its meaning, and document the return value as a
TransformerPPOBrainConfig under Returns; keep concise and follow NumPy-style
sections (Parameters, Returns) and include types and a one-line description;
place this docstring directly above the function definition for _config in the
test file so tools and linters recognize it.
🊄 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: c71c8929-e25f-49f4-91ce-3b8296cbfa4e

ðŸ“Ĩ Commits

Reviewing files that changed from the base of the PR and between 43d673c and face52d.

📒 Files selected for processing (13)
  • AGENTS.md
  • CONTRIBUTING.md
  • README.md
  • configs/scenarios/foraging/transformerppo_small_klinotaxis.yml
  • openspec/changes/add-continuous-2d-and-action-heads/design.md
  • openspec/changes/add-continuous-2d-and-action-heads/tasks.md
  • openspec/config.yaml
  • packages/quantum-nematode/quantumnematode/brain/arch/__init__.py
  • packages/quantum-nematode/quantumnematode/brain/arch/dtypes.py
  • packages/quantum-nematode/quantumnematode/brain/arch/transformer_ppo.py
  • packages/quantum-nematode/quantumnematode/utils/config_loader.py
  • packages/quantum-nematode/tests/quantumnematode_tests/brain/arch/test_transformer_ppo.py
  • scripts/run_simulation.py


Add a Transformer/attention PPO brain (`transformer-brain`) through the plugin interface as the **G2.b/G2.c verification** (files-touched â‰Ī 6; no per-arch branches), recording engineer-hours for G2.a. It doubles as the roadmap's MAY transformer comparison row. To make the no-per-arch-branch claim hold at the entrypoint too, refactor the `match brain_type` CLI-default block ([`scripts/run_simulation.py:286-298`](../../../scripts/run_simulation.py#L286)) into a registry-driven default. **This refactor ships in a SEPARATE prior commit** — it is a one-time platform fix, not a per-architecture cost — so it is NOT counted in the transformer-addition `git diff --name-only` that measures G2.b. **Files-budget measurement (reconciling the two enumerations):** G2.b's threshold is `â‰Ī 6 files`; its parenthetical list in [`phase6-tracking` design.md Decision 6](../phase6-tracking/design.md) ("registry registration + brain implementation + config class + config example + smoke test + docs") is illustrative. The *operative* enumeration is the [plugin-developer-guide](../../../docs/architecture/plugin-developer-guide.md) budget: new module (brain + config class + `@register_brain`), `dtypes.py` enum member, `brain/arch/__init__.py` import/export, `config_loader.py` `BrainConfigType` union, `brain_factory.py` `_build_infra_kwargs` branch *only if non-default*, and a test file = â‰Ī 6, with the YAML config example explicitly *outside* the 6 — as are the cross-repo brain-arch enumeration updates (`AGENTS.md`, `README.md`, `CONTRIBUTING.md`, `openspec/config.yaml`, currently "24 architectures"), which are required for merge but are platform-bookkeeping, not per-architecture parity cost (landed in a separate commit from the runtime wiring so the G2.b `git diff` stays clean). The â‰Ī 6 threshold itself is unchanged (this is a pre-fire measurement clarification, not a gate recalibration). **Why a real arch over a hypothetical baseline:** the user chose the transformer to get a reusable comparator; G2.a explicitly makes engineer-hours non-load-bearing precisely so a heavier arch doesn't distort the parity verdict. **Gate-2 floor check (G2.d):** connectome + MLP-PPO train cleanly on continuous-2D klinotaxis to â‰Ĩ 50 % of their T4 grid-substrate mean episode return (the 50 % floor is wide because discrete-grid and continuous-2D return scales aren't directly comparable; this checks "didn't break", not ranking).

**Implementation note (2026-06-07):**

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 | ðŸŸĄ Minor | ⚡ Quick win

Fix future-dated implementation note timestamp.

Line 89 says 2026-06-07, which is ahead of the current timeline (2026-06-06) and makes the record look out-of-order. Please use the actual completion date or remove the date until that day passes.

ðŸĪ– 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/add-continuous-2d-and-action-heads/design.md` at line 89,
The implementation note header currently reads "Implementation note
(2026-06-07):" which is a future date; update that header in the document (the
line containing "Implementation note (2026-06-07):") to either the actual
completion date "2026-06-06" or remove the parenthetical date entirely so the
record is not out-of-order.

@codecov

codecov Bot commented Jun 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.87879% with 28 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...tode/quantumnematode/brain/arch/transformer_ppo.py 87.77% 16 Missing and 12 partials ⚠ïļ

ðŸ“Ē Thoughts on this report? Let us know!

Address PR #212 review:
- TransformerPPOBrain now defensively copies action_set on store (__init__ +
  setter): `self._action_set = list(action_set)`. Prevents the shared
  module-level DEFAULT_ACTIONS default arg from being mutated across instances —
  matches the CfC/connectome precedent. (Signature kept = DEFAULT_ACTIONS for
  consistency with all sibling brains; the store-copy fully addresses the leak.)
- Add a NumPy-style docstring to the `_config` test helper.

Skipped: the design.md "Implementation note (2026-06-07)" date — verified
`date` is actually 2026-06-07 (the session date rolled mid-work), so it is the
real authoring date and chronological with the 2026-06-06 §4 notes, not a future
or out-of-order date.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chrisjz
chrisjz merged commit 287ed3d into main Jun 6, 2026
3 checks passed
@chrisjz
chrisjz deleted the openspec/t5-transformer-gate2 branch June 6, 2026 14:49
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