research spec

06 — Bounded-Growth Consolidation: Trained Encoder + Hypernetwork Adapters

File
06-bounded-growth-consolidation.md
Size
15.5 KB
SHA-256
84992d5341c36dff…
Primary source. This is the verbatim Oczy document. The analytical field notes on the research page interpret and summarize these sources.

06 — Bounded-Growth Consolidation: Trained Encoder + Hypernetwork Adapters

Stop pickling dense random matrices and raw traces; make persistent memory a compact, trained latent identity whose byte-cost stops growing.

Status: ACCEPTED (2026-07-11, five-seed) | Thesis anchor: experiments.txt §5 (hypernetwork identity, z_{t+1}=z_t+Δz), §9 (experience autoencoder; L = behavior_improvement + reconstruction_of_correction + compression_penalty + anti-overgeneralization + replay_consistency) | Goal anchor: GOALS.md Goal 3 (organ tensor upgrades / metabolism loop closure) | Depends on / relates to: 01-correction-to-competence-benchmark (owns the byte metric), 05-metabolism-loop-closure (the consolidation path), 07-conversation-world-model-rl

Outcome (2026-07-11): ACCEPTED (five-seed). Campaign 0d48130 (kaggle CPU-only, commit 0d48130): bounded_growth_m1_ratio=0.002079 across 5 seeds with zero variance. Structural footprints are bit-identical across seeds. bytes_per_delta spread ≤20 B: A0 18 B, A0b 20 B, A1 19 B, A2 18 B, A3 19 B. The M1 ratio (combined-footprint reduction) is well under the 1/10 threshold. Evidence: ../experiments_logs/2026-07-11_campaign_0d48130.md.

Problem

The organism stores experiences instead of metabolizing them, and it pays for it in bytes. On the eval-suite curriculum, OrganismAgent reports memory_bytes_per_behavior_delta = 68772.0 versus FastOnlyAgent's 12.0 (NOTES.md:253,256) — a ~5,700x spread for, by NOTES.md's own description, "matching FastOnlyAgent on forgetting/consolidation/identity" and adding only "a small transfer edge" (transfer 0.25 vs 0.1667; NOTES.md:256-260). The metric is consolidated_size / max(1, successful_lessons) where consolidated_size = _memory_bytes(agent) (the full OrganismAgent.memory_bytes()) and successful_lessons counts fixed_after_correction corrections (eval_suite.py:393-396; organism.py:482-495). NOTES.md:261 attributes the cost to "the hippocampus + immune + autoencoder pickled state."

Two of the dominant contributors are the exact organs this project owns, and both are honest about being prototypes:

  • ExperienceAutoencoder is "an untrained random projection" (EVALUATION.md:36-38), scoring aggregate 0.203 (EVALUATION.md:14). The active Δz is tiny — LATENT_DIM=32, 256 bytes at float64 (autoencoder.py:24; EVALUATION.md:36) — but the persisted organ is dominated by _A, a dense (RESIDUAL_DIM=28, NUM_SOURCES*MAX_VOCAB=1024) Gaussian sensing matrix = 28,672 floats ≈ 229 KB at float64 (autoencoder.py:118-123). Crucially, in the default correction flow OrganismAgent calls experience_autoencoder.encode(episode)not train_step (organism.py:415-416) — so _A is never mutated and stays the column-normalized matrix that is deterministically regenerable from seed=42 (autoencoder.py:348-350), yet status(include_size=True) pickles all ~229 KB of it anyway (autoencoder.py:608-610; common/bytes.py mem_bytes). (encode() does grow the small token-vocab dict as a side effect, but _A itself is untouched.)

  • IdentityHypernetwork scored aggregate 0.001 in the original evaluation (EVALUATION.md:15), which described a fixed 14-entry CONCEPT_VOCABULARY that "does not include the 30 curriculum senses, so adapter retrieval fails completely" (EVALUATION.md:40-42; hypernet.py:22-37). That EVALUATION.md measurement predates the grow_vocab / auto-register fix now present in the code (hypernet.py:231-291, _extract_first_concept auto-grow at hypernet.py:310-327): update_identity now registers unknown sense tokens on the fly, so the hard "retrieval fails completely" blocker is removed, and the re-validated score moved to 0.006 (NOTES.md:126) — with NOTES.md attributing the residual low score partly to an eval-probe artifact (a single concept token compared against a multi-word sense label; NOTES.md remaining-gaps #1), not to the closed vocabulary. The live problem this project targets is the growth shape of the fix: grow_vocab appends one full W row (input_dim = 4*latent_dim floats) per new concept (hypernet.py:231-291), so the byte-cost grows linearly with the number of distinct corrections — the opposite of bounded growth (it is only re-bounded when the vocab exceeds max_concepts=1000 and the oldest rows are pruned; hypernet.py:100-101,270-291). EVALUATION.md takeaway #3 (lines 52-53) prescribes "replace the fixed concept list with an open embedding layer."

The thesis is explicit that this should be bounded: "the system can have thousands of corrections, but the active agent only carries a compressed latent identity plus a small replay buffer" (experiments.txt §9, line 525). The north-star metric is behavior_delta_per_byte_of_persistent_memory (rl_pipeline_design.md:342); the eval suite measures its inverse (eval_suite.py:396). Today the organism violates the thesis: its footprint is a dense regenerable matrix plus per-concept rows plus pickled raw traces.

Hypothesis

  1. Bounded growth is reachable on the controllable organs. A trained compact encoder plus a trained concept-embedding hypernetwork can cut the combined serialized footprint of experience_autoencoder + identity_hypernetwork by ≥10x versus the current random-projection / per-concept-row implementation, while reconstruction fidelity rises above the 0.203 random-projection baseline (EVALUATION.md:14) and the eval-suite behavior scores (transfer/scope/forgetting/identity) do not drop below the current OrganismAgent (NOTES.md:256).
  2. Most of the autoencoder bloat is serialization, not information. Because _A is never trained in the default flow (organism.py:415-416), simply persisting seed + Hebbian-deltas instead of the dense matrix recovers a large fraction of the byte savings with zero behavioral change — separating "store less" from "learn better."

Falsifier for (1): if no trained condition beats 2x combined-byte reduction, or if compression drops forgetting_score or identity_drift_score below 1.0, the trained-adapter direction is closed. Falsifier for (2): if A0b (seed-regenerable) is not byte-identical in behavior to A0, the "never trained" assumption is wrong and must be re-derived.

Why now / what unblocks it

  • The metric already exists and is not saturated: unlike the headline accuracy metrics (forget/consol/identity pinned at 1.0 across runs — the documented saturation meta-problem), memory_bytes_per_behavior_delta spans 12 → 68,772 (NOTES.md:251-256) and is computed end-to-end by eval_suite.py:396. This project lives entirely on the un-saturated axis.
  • Both target organs are pure NumPy with clean status/byte contracts (status(include_size=True)["serialized_bytes"], common/bytes.py), so the experiment needs no LFM2.5 driver — the bloat is in organ serialization, not the LM. This sidesteps the cvec/prefix steering ceiling entirely (relates to 02/03/04).
  • The mechanism is identified: the dense random sensing matrix is seed-regenerable (organism.py:415-416 never calls train_step), and the hypernetwork's per-concept-row growth is the named open-embedding fix (EVALUATION.md:52-53). Both are small, local replacements. Note the swap is not a pure config change: config["experience_autoencoder"] is passed positionally into the fixed ExperienceAutoencoder constructor and config["identity_hypernetwork"] is splatted as kwargs into the fixed IdentityHypernetwork constructor (organism.py:63-69) — those keys configure the existing classes, they do not accept a prebuilt/alternative organ. Injecting a trained replacement therefore means either replacing the constructed agent.experience_autoencoder / agent.identity_hypernetwork attribute after OrganismAgent.__init__, or a small constructor extension that accepts a prebuilt organ instance.

Approach

Tied to thesis §5 and §9. Replace two prototype organs with trained-but-tiny versions and measure the byte/behavior tradeoff under matched-pair controls.

  • Trained compact encoder (§9). Train an offline encoder that maps a correction episode → Δz minimizing the thesis-9 composite loss (reconstruction of the correction signal + compression penalty + anti-overgeneralization + replay consistency). Persist only the small trained weights, not a 1024-column dense random matrix. The decoder target stays the existing decode() field set (corrected_behavior_hint / failure_class / trigger_conditions / counterexamples; autoencoder.py:264-269) so reconstruction is scored on the same surface as today.
  • Seed-regenerable control (§9, cheap). A no-training variant that persists seed + Hebbian-delta sparse updates and regenerates _A on load. Isolates "don't pickle the dense matrix" from "train a better encoder." (In the default flow there are zero Hebbian deltas because train_step is never called, so this control reduces to persisting only the seed.)
  • Concept-embedding hypernetwork (§5). Replace the per-concept W-row vocabulary (which grows linearly per concept; hypernet.py:231-291) with a fixed-size shared concept embedding E so adapter = HyperNet(z) is constant-size in the number of concepts. Corrections update z, not the parameter count — exactly z_{t+1}=z_t+Δz with bounded footprint (experiments.txt §5).
  • Bounded-growth instrumentation. Sample agent.memory_bytes() after every correction and fit the marginal byte slope; the thesis claim is that the slope flattens to ~0 after warmup, vs the current linear grow_vocab growth.
  • Per-organ decomposition first. Before claiming whole-organism wins, measure each organ's serialized_bytes (organism.py _module_bytes, lines 554-588) to confirm how much of the 68,772 the two target organs actually own (NOTES.md:261 says hippocampus also dominates — see Risks).

Success criteria

Behavioral and measurable; all on the eval-suite curriculum at a fixed seed.

  • PRIMARY — combined controllable footprint. serialized_bytes(experience_autoencoder) + serialized_bytes(identity_hypernetwork) for the full trained condition (A3) is ≤ 1/10 of condition A0. (Discriminating: today ~229 KB autoencoder alone; not saturated.)
  • SECONDARY — whole-organism byte/delta. memory_bytes_per_behavior_delta improves by ≥2x vs A0's 68,772 (eval_suite.py:396). Reported, not gated above 2x, because the hippocampus also contributes (honest ceiling — see Risks).
  • GUARD — behavior preserved. transfer_score ≥ 0.25, scope_score ≥ 0.1667, forgetting_score = 1.0, identity_drift_score = 1.0 (must not fall below OrganismAgent A0; NOTES.md:256). The two = 1.0 guards are floors against regression, not discriminating success axes (both are saturated for the current organism).
  • QUALITY — reconstruction. Held-out reconstruction fidelity (eval_extended.py:432-437 reconstruction_error component, 1 - mean(reconstruction_error)) > 0.203 for the trained encoder (beats random projection; EVALUATION.md:14,36).
  • BOUNDEDNESS — growth slope. Marginal bytes / correction for A3 over the curriculum ≤ 100 B/correction after a 3-correction warmup (vs linear grow_vocab growth).

Kill criteria. (a) Best trained condition combined-footprint reduction < 2x → trained adapters do not pay for themselves. (b) Any compressed condition drops forgetting_score or identity_drift_score below 1.0 → over-compression causes catastrophic forgetting (the anti-overgeneralization term failed). (c) Reconstruction fidelity ≤ 0.203 → training did not beat the random projection. (d) Per-organ decomposition shows the two target organs own < 30% of consolidated_size → the bloat is the hippocampus; rescope to trace compression and hand off to 05-metabolism-loop-closure.

Risks & open questions

  • The hippocampus may dominate. NOTES.md:261 names hippocampus + immune + autoencoder together. If raw-trace pickling owns most of consolidated_size, compressing only these two organs caps the whole-organism win well under 10x. Mitigation: per-organ decomposition is step 0; the secondary metric is explicitly a ≥2x (not ≥10x) target, and kill-criterion (d) makes the handoff explicit.
  • _A-untrained assumption. If any code path calls train_step during the eval (it appears not to; organism.py:415-416 uses encode), the seed-regenerable control A0b breaks. Measured directly by A0-vs-A0b byte-identity of behavior.
  • Compression vs scope. scope_score is already low (0.1667; NOTES.md:256) and is the metric most likely to collapse under aggressive latent_dim reduction — the thesis anti-overgeneralization penalty is supposed to defend it, but the toy cortex's documented "two-senses-per-token" failure (Stage 2 fails 100%, NOTES.md:243) may bound scope regardless of encoder quality.
  • Open: what latent_dim (8/16/32) maximizes behavior_delta_per_byte? Does the concept-embedding hypernetwork recover transfer, or — given grow_vocab already removed the hard retrieval blocker (NOTES.md:126) and the residual gap is partly an eval-probe artifact (NOTES.md remaining-gaps #1) — does the eval curriculum's small concept set make a fixed-size embedding indistinguishable from the (now-unbounded) auto-grown list except on bytes?
  • Open: is "behavior delta" (the denominator, fixed_after_correction) stable across compression? If compression changes which lessons get fixed, the ratio confounds bytes with behavior — controlled by holding the curriculum seed and reporting the denominator separately.

Prior evidence

  • NOTES.md:251-261 — baseline table: OrganismAgent 68,772.0 B/Δ vs FastOnlyAgent 12.0 B/Δ; "Memory cost is dominated by the hippocampus + immune + autoencoder pickled state."
  • EVALUATION.md:14-15,36-42,52-53 — ExperienceAutoencoder aggregate 0.203 ("Δz tiny … encoder is an untrained random projection"); IdentityHypernetwork 0.001 ("fixed CONCEPT_VOCABULARY does not include the 30 curriculum senses") — superseded in current code by the grow_vocab fix (re-validated 0.006, NOTES.md:126); takeaway to "replace the fixed concept list with an open embedding layer."
  • autoencoder.py:24-31,118-123,531-573,608-610 — LATENT_DIM=32, RESIDUAL_DIM=28, MAX_VOCAB=256; _make_sensing_matrix = (28,1024) Gaussian (28,672 floats ≈ 229 KB float64); train_step Hebbian rank-1 on _A; status(include_size=True) pickles whole organ.
  • organism.py:415-419,482-495,554-588 — correction flow calls encode (not train_step) and update_identity; memory_bytes() sums six organs via _module_bytesserialized_bytes.
  • hypernet.py:22-37,90,100-101,107-115,231-291,310-327 — fixed 14-concept seed vocab; W = (output_dim, 4*latent_dim); generate_adapters = W @ z; grow_vocab appends one W row per concept (auto-invoked from _extract_first_concept), capped at max_concepts=1000.
  • eval_suite.py:393-396,445-448 — successful_lessons = fixed_after_correction; memory_bytes_per_delta = consolidated_size / max(1, successful_lessons); raw_trace_size/consolidated_size = _memory_bytes(agent).
  • eval_extended.py:406-443 — autoencoder eval: compression_ratio (raw-JSON len(json.dumps(episode)) / latent dz.nbytes, which ignores the persisted _A; normalized as min(ratio,50)/50), reconstruction_error = 1 - mean, mem_bytes(ae), aggregate = 0.5·compression + 0.5·reconstruction.
  • rl_pipeline_design.md:342 — north-star behavior_delta_per_byte_of_persistent_memory.
  • experiments.txt §5 (lines 307,508), §9 (lines 516-520,525) — hypernetwork identity (z_{t+1}=z_t+Δz, adapter_weights = HyperNet(z_user,z_domain,z_style,z_mistakes)) and experience-autoencoder loss / bounded-growth rationale.